http://acm.hdu.edu.cn/showproblem.php?pid=4284
这题后台数据相当强呀
思路:先用flody 求得任意两点间的最短距离
然后再用 状态压缩+DP
dist [ i ] [ j ] 表示已经到 i 状态压缩表示的城市 拿到证件 打工 而且现在在 第 j 个要去的城市 这种情况下剩余的最多钱
不断更新 最后看是否有满足条件 去过所以要去的城市 而且还能回到原点的 状态
代码及其注释:
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <algorithm> #define LL long long using namespace std; const int N=103; const int H=16; const int INF=0x0fffffff; int dist[1<<16][H];//此状态剩余最多钱 int d[N][N];//最短路 struct node { int k,c,d; }mem[H];//要去的城市相关信息 bool cmp(node x,node y)//按城市序号排序 主要是让原点 在 0 位置 { return x.k<y.k; } int main() { //freopen("data.txt","r",stdin); int T; scanf("%d",&T); while(T--) { int n,m,money,h,K;; scanf("%d %d %d",&n,&m,&money); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) { if(i==j)d[i][j]=0; else d[i][j]=INF;//初始化 } while(m--) { int i,j,p; scanf("%d %d %d",&i,&j,&p); d[i][j]=d[j][i]=min(d[i][j],p); } scanf("%d",&h); int have1=false; for(int i=0;i<h;++i) { scanf("%d %d %d",&mem[i].k,&mem[i].c,&mem[i].d); if(mem[i].k==1) have1=true; } if(!have1)//如果原点不在则将原点加入 这样不会影响最终结果 而且便于运算 { mem[h].c=mem[h].d=0; mem[h].k=1;++h; } sort(mem,mem+h,cmp); for(int l=1;l<=n;++l) for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(d[i][j]>d[i][l]+d[l][j]) d[i][j]=d[i][l]+d[l][j];//求最短路 memset(dist,-1,sizeof(dist)); K=(1<<h)-1; dist[0][0]=money;//在没有去过任何城市时 在第0个要去的城市(原点)时所有钱 for(int i=0;i<K;++i) { for(int j=0;j<h;++j)//i 和 j表示的顺序不能变 因为更新时是想i 变大的方向更新 { if(dist[i][j]==-1) continue; for(int l=0;l<h;++l) { int temp=i|(1<<l); if(temp==i)//已经去过 拿证件 打工 continue; int more=dist[i][j]-d[mem[j].k][mem[l].k]-mem[l].d;//看是否可以成功到达 并且拿到证件 if(more<0) continue ; dist[temp][l]=max(dist[temp][l],more+mem[l].c);//更新 } } } bool ans=false; for(int i=0;i<h;++i) if(dist[K][i]>=d[mem[i].k][mem[0].k])//去过所有要去的城市 而且能回到原点 {ans=true;break;} if(ans) printf("YES\n"); else printf("NO\n"); } return 0; }