因为城市数量太多而且 不是选中的城市在中间过程考虑 情况会很复杂而且只会有花费的增加并不会改变有花费的改变。。。所以用最短路floyd把结果记录下来就好不需要再考虑中间过程,在状压DP时直接在选中的城市之间转移就好,还有只有有钱买证才能得到钱
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 16;
const int inf = 0x3f3f3f3f;
typedef long long LL;
int dp[1<<N][20], s[110][110], work[20], v[20], w[20];
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n, m, money;
scanf("%d %d %d", &n, &m, &money);
memset(dp,-1,sizeof(dp));
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
s[i][j]=inf;
}
s[i][i]=0;
}
for(int i=0; i<m; i++)
{
int x, y, c;
scanf("%d %d %d", &x, &y, &c);
x--,y--;
if(s[x][y]>c)
s[x][y]=s[y][x]=c;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
for(int k=0;k<n;k++)
{
if(j!=i&&k!=i&&k!=j&&s[j][k]>s[j][i]+s[i][k])
s[j][k]=s[j][i]+s[i][k];
}
}
}
int num;
scanf("%d",&num);
int pos=-1;
for(int i=0; i<num; i++)
{
scanf("%d %d %d",&work[i], &v[i], &w[i]);
work[i]--;
if(work[i]==0)
pos=i;
}
if(pos==-1)
{
work[num]=0,v[num]=0,w[num]=0;
pos=num++;
}
if(money>=w[pos])
dp[1<<pos][pos]=money-w[pos]+v[pos];
dp[0][pos]=money;
for(int i=0;i<(1<<num);i++)
{
for(int j=0;j<num;j++)
{
if(dp[i][j]==-1)
continue;
for(int k=0;k<num;k++)
{
if(k==j||(i&(1<<k))||s[j][k]==inf)
continue;
int state=i|(1<<k);
if(dp[i][j]>=w[k]+s[work[j]][work[k]])
dp[state][k]=max(dp[state][k],dp[i][j]-s[work[j]][work[k]]-w[k]+v[k]);
}
}
}
int ans=-1;
for(int i=0;i<num;i++)
{
if(dp[(1<<num)-1][i]>=s[work[i]][0])
ans=1;
}
if(ans==-1)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}
参考:
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#define inf 1<<28
#define N 105
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,money,h;
int path[N][N];
int dp[20][1<<16];
int work[20],c[20],d[20];
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&money);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)
path[i][j]=inf;
path[i][i]=0;
}
for(int i=0;i<m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
u--;v--;
path[u][v]=Min(path[u][v],w);
path[v][u]=path[u][v];
}
//Floyd预处理
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(i!=k&&i!=j&&j!=k)
path[i][j]=Min(path[i][k]+path[k][j],path[i][j]);
scanf("%d",&h);
int pos=-1;
for(int i=0;i<h;i++){
scanf("%d%d%d",&work[i],&c[i],&d[i]);
work[i]--;
if(work[i]==0) pos=i; //说明必需点中包含了起点1
}
//如果不包含,我们加入冗余点,便于后面处理,c和d都为0
if(pos==-1){
work[h]=0;c[h]=0;d[h]=0;
pos=h++;
}
memset(dp,-1,sizeof(dp));
if(money-d[pos]>=0) dp[pos][1<<pos]=money-d[pos]+c[pos];dp[pos][0]=money;
for(int i=0;i<(1<<h);i++){
for(int j=0;j<h;j++){
if(dp[j][i]==-1) continue;
for(int k=0;k<h;k++){
if(k==j||((1<<k)&i)) continue;
//钱够在两个城市之间移动,而且够买证
if(dp[j][i]>=path[work[j]][work[k]]+d[k])
dp[k][i|(1<<k)]=Max(dp[k][i|(1<<k)],dp[j][i]-path[work[j]][work[k]]-d[k]+c[k]);
}
}
}
bool ans=false;
for(int i=0;i<h;i++)
//最后判断能不能返回起点
if(dp[i][(1<<h)-1]>=path[work[i]][0]){
ans=true;
break;
}
puts(ans?"YES":"NO");
}
return 0;
}