A sample Hamilton path (经典状压dp,哈密顿路径)

 

Give you a Graph,you have to start at the city with ID zero.

Input

The first line is n(1<=n<=21) m(0<=m<=3) 
The next n line show you the graph, each line has n integers. 
The jth integers means the length to city j.if the number is -1 means there is no way. If i==j the number must be -1.You can assume that the length will not larger than 10000 
Next m lines,each line has two integers a,b (0<=a,b<n) means the path must visit city a first. 
The input end with EOF. 

Output

For each test case,output the shorest length of the hamilton path. 
If you could not find a path, output -1 

Sample Input

3 0
-1 2 4
-1 -1 2
1 3 -1
4 3
-1 2 -1 1
2 -1 2 1
4 3 -1 1
3 2 3 -1
1 3
0 1
2 3

Sample Output

4
5


        
  

Hint

I think that all of you know that a!=b and b!=0    =。=

题意:求从0点开始的最短哈密顿路径,要求某些点必须在某些点之前经过(每个点只走一次,走完所有点)

思路:状压dp,dp[sta][end]表示已经走过的点的集合状态是sta,走过的最后一个点是end 的最短距离

#include<bits/stdc++.h>
//#include<cstdio>
//#include<algorithm>
//#include<cstring>
//#include<map>
//#include<iostream>
#pragma GCC optimize(3)
#define max(a,b) a>b?a:b
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
int dp[1<<21][22];//dp[sta][end] 当前走过点的状态是sta,最后一个走过的点是end 
int mp[25][25];
vector<int> pre[25];
int main(){
	ios::sync_with_stdio(false);
    cout.tie(NULL);
    int n,m;
    while(~scanf("%d%d",&n,&m)){
    	for(int i=0;i<n;i++){
    		for(int j=0;j<n;j++){
    			scanf("%d",&mp[i][j]);
    			if(mp[i][j]==-1) mp[i][j]=INF;
			}
		}
		int up=(1<<n)-1;
		for(int i=0;i<n;i++) vector<int>().swap(pre[i]);
	    for(int i=0;i<=up;i++) {
	    	for(int j=0;j<n;j++){
	    		dp[i][j]=INF;
			}
		}
	    for(int i=1;i<=m;i++){
	    	int a,b;
	    	scanf("%d%d",&a,&b);
	    	pre[b].push_back(a);
		}
		dp[1][0]=0;
		for(int i=0;i<=up;i++){//枚举状态(走过哪些点) 
			for(int j=0;j<n;j++){//枚举最后一个走过的点 
				if(dp[i][j]==INF) continue;//该状态还没走过
				if((i&(1<<j))==0) continue;//j没走过 (用不到,不加也能过,上一个判断已经包含了)
				for(int k=0;k<n;k++){//枚举下一个要走的点 
					if(i&(1<<k)) continue;//k点已经走过了
					bool flag=true;
					for(int v=0;v<pre[k].size();v++){//判断限定条件 
						int &p=pre[k][v];
						if((i&(1<<p))==0) {//前节点没走过
						   flag=false;
						   break;
					    }
					} 
					if(flag) dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+mp[j][k]); 
				} 
			}
		}
		int ans=INF;
		for(int i=0;i<n;i++) {
			ans=min(ans,dp[up][i]);
		}
		if(ans==INF) printf("-1\n");
		else printf("%d\n",ans);
	}
	return 0;
}




 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值