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;
}