题意: 给出n个城市,m条路, 以及路的花费,
要求给定的h个城市,需要先花di钱 买个工作证, 再转ci元钱。询问是否可以得到所有的工作证并且从1出发,逛完所有的选定h点,再返回1
思路:
floyd求两两最短距离
dp初始化为-1.
DP[STA][I] 表示在sta 状态下, 最后一个城市是第i个,可以省下的最多钱数
#include <cstring>
#include <algorithm>
#include <stdio.h>
#include <iostream>
using namespace std;
int mp[105][105];
const int inf=0x3f3f3f3f;
void floyd(int n)
{
for(int k=1; k<=n; k++)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
}
}
}
int num[150], ci[105],di[105];
int h;
int dp[(1<<15)+2][16];
bool bfs(int mon)
{
int tmp;
for(int i=0; i<h; i++)
{
tmp=mon-mp[1][num[i]]-di[i];
if(tmp>=0)
dp[1<<i][i]=tmp+ci[i];
}
int st=(1<<h)-1;
for(int i=1; i<=st; i++)
for(int j=0; j<h; j++)
{
if(!(i&(1<<j)))continue;
for(int k=0; k<h; k++)
{
if(i&(1<<k))
continue;
tmp=dp[i][j]-mp[num[j]][num[k]]-di[k];
if(tmp>=0)
{
tmp+=ci[k];
int stat=i+(1<<k);
dp[stat][k]=max(dp[stat][k],tmp);
}
}
}
int flag=0;
for(int i=0;i<h;i++)
{
tmp=dp[st][i]-mp[num[i]][1];
if(tmp>=0){flag=1;break;}
}
if(flag) return true;
return false;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
for(int i=0; i<101; i++)
for(int j=0; j<101; j++)
mp[i][j]=inf;
for(int i=0;i<101;i++)
mp[i][i]=0;
memset(dp,-1,sizeof dp);
int n,m,s;
scanf("%d%d%d",&n,&m,&s);
for(int i=1; i<=m; i++)
{
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
mp[u][v]=min(mp[u][v],k);
mp[v][u]=min(mp[v][u],k);
}
floyd(n);
scanf("%d",&h);
for(int i=0;i<h;i++)
scanf("%d%d%d",&num[i],&ci[i],&di[i]);
if(bfs(s))printf("YES\n");
else printf("NO\n");
}
}