给你n个国家,m条路线:u_i与v_i之间的距离w_i。
输出从1号国家出发经过每个国家至少一次再回到1号国家的最短距离。
【官方题解】:
我们首先需要预处理出任意两个国家之间的最短距离,因为数据范围很小,所以直接用Floyd就行了。
之后,我们用f[S][i]表示访问国家的情况为S,当前最后访问的一个国家是i所需要的最小总油量,其中,S的二进制表示记录了访问国家的情况,S在二进制表示下的第i位(不管是从左往右还是从右往左都可以)。如果是1则表示第i个国家被访问过了,否则表示第i个国家没有被访问过。
如dp[13][3](13=1101表示现有城市1、3、4)表示从城市1到城市3的最短路(可能经过城市4)。
那么状态转移方程:
f[S|(1<<i)][i]=min(f[S][j]+f[i][j]),
其中,S这个状态不包含i城市但包含j城市。即i和j满足S&(1<<j)=1且S&(1<<i)=0。
最开始时,除了f[1][1]是0,其他情况都是无穷大,之后先枚举S,再枚举i(我验题的时候因为这里搞反结果WA了),那么最终的答案就是
min(f[(1<<n)-1][i]+f[i][1]),其中i∈ [2,n]。
总复杂度为O(n^3+n^2*2^n)
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <string> #include <stack> #include <queue> #include <set> #include <map> typedef long long ll; using namespace std; const int inf=0x3f3f3f3f; const int maxn=1e6+10; int m,n; int g[20][20]; int dp[maxn][20]; void floyd(){ for(int k=1;k<=n;++k){ for(int i=1;i<=n;++i){ for(int j=1;j<=n;++j){ g[i][j]=min(g[i][j],g[i][k]+g[k][j]); } } } } int main() { int t; scanf("%d",&t); while(t--){ memset(g,inf,sizeof g); scanf("%d %d",&n,&m); int u,v,w; for(int i=0;i<m;i++){ scanf("%d %d %d",&u,&v,&w); if(g[u][v] > w){ g[u][v]=g[v][u]=w; } } if(n == 1){ cout<<0<<endl; continue; } floyd(); for(int i=1;i<=n;++i)g[i][i]=0; memset(dp,inf,sizeof dp); dp[1][1]=0; for(int s=1;s< (1<<n) ; ++s ){ for(int i=1;i<=n;++i){ if(s&(1<<(i-1))){//s中包含i for(int j=1;j<=n;++j){ if( (s&(1<<(j-1)))==0 ){//s中不包含j int tt= (s | (1<<(j-1)) );//tt为s包含j的状态 dp[tt][j]=min(dp[tt][j],dp[s][i]+g[i][j]); } } } } } //printf("%d\n",g[n][1] ); int ans=inf; for(int i=2;i<=n;i++) ans=min(ans,dp[ (1<<n) -1 ][i]+g[i][1]); printf("%d\n",ans); } }
时间: 2024-10-11 06:47:42