POJ3311:状态压缩DP

POJ3311

题意

  • 从0出发经过所有的点(可以重复)再回到0的最短时间

题解

  • dp[i][j]表示在i状态到达j的最短时间。最后答案是min(dp[1<<(n-1)][1…n] + mp[1…n][0])
  • floyd求出任意两点之间的最短时间。
  • 状态转移:找一个中转站k,再到目的地j,其中状态i包含j和k。dp[i][j] = min(dp[i][j],dp[i^(1<<(j-1))][k] + mp[k][j])。如果没有中转站,那么直接到达j,dp[i][j] = mp[0][j]。i^(1<<(j-1))表示没有经过j的状态。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int const N = 10 + 5;
int const inf = 0x7f7f7f7f;
int n,ans;
int mp[N][N];
int dp[1<<N][N];  //dp[i][j]表示i状态经过j的最短时间
int main(){
	while(~scanf("%d",&n) && n){
		memset(dp,0,sizeof(dp));
		for(int i=0;i<=n;i++)
			for(int j=0;j<=n;j++)
				scanf("%d",&mp[i][j]);
		for(int k=0;k<=n;k++)
			for(int i=0;i<=n;i++)
				for(int j=0;j<=n;j++)
					if(mp[i][k] < inf && mp[k][j] < inf && mp[i][j] > mp[i][k] + mp[k][j])
						mp[i][j] = mp[i][k] + mp[k][j];
		memset(dp,inf,sizeof(dp));
		for(int i=0;i<(1<<n);i++){   //枚举状态
			for(int j=1;j<=n;j++){   //求到j的最短时间
				if(i & (1<<(j-1)) == 0)	continue;   //没有经过点j
				if(i == (1<<(j-1)))		dp[i][j] = mp[0][j];  //如果只经过点j,那么最短距离为从0直接到j
				else{
					for(int k=1;k<=n;k++){
						if(j != k && i&(1<<(k-1)))		//如果没有经过点k
							dp[i][j] = min(dp[i][j],dp[i^(1<<(j-1))][k] + mp[k][j]);
					}
				}
			}
		}
		ans = inf;
		for(int i=1;i<=n;i++)
			ans = min(ans,dp[(1<<n)-1][i] + mp[i][0]);
		printf("%d\n",ans);
	}
	return 0;
}

 

猜你喜欢

转载自blog.csdn.net/weixin_42264485/article/details/89061424