题意:一个国家有最多200个城市。一个人住在城市1,他想去其中15个城市打工。每次打工先要交手续费,然后才能挣一些钱。从一个城市到另一个城市需要花一定路费。问这个人能否从1城市出发把他所有想打工的城市都打一遍工,然后回到1.
思路:最多15个城市,所以可以考虑状态压缩。先把15个城市之间两两距离预处理出来。然后每到达一个城市把状态中这个城市对应的位置变成1。
我用的是队列来辅助状态转移。
最开始做这道题的时候一直超时,后来把队列弄成全局变量就变成了re。看来最好还是不要把队列弄成全局变量。改回来以后还是超时。跟别人的代码对比了一下,发现是状态转移写的太冗余了。
假设到达其中一个城市i的状态是s,我考虑了从i走向其他城市,不在那个城市打工,仅仅路过这种情况。事实上这种情况是不用考虑的,考虑路过这种情况仅仅是在不知道每两个点之间最短距离的时候用。但是预处理中已经把两两之间最短距离求出来了,所以这样的考虑完全不需要,只是浪费时间。
因为超时怀疑是标准库效率不高,还自己实现了一个队列。。。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<math.h>
#include<iostream>
#include<cstring>
#include<map>
#include<set>
#include<list>
#include<stack>
#include<algorithm>
#include<queue>
#include<vector>
#include<time.h>
using namespace std;
const int inf = 100000000;
int id[200];
int ci[200];
int di[200];
int icity[20];
int dp[20][1<<17];
int dis[200][200];
bool in[20][1<<17];
struct STA{
int city;
int s;
STA(int _c = 0,int _s = 0):city(_c),s(_s){}
};
void Floyd(int n)
{
for(int i = 1;i<=n;++i){
dis[i][i]=0;
}
for(int k = 1;k<=n;++k){
for(int i=1;i<=n;++i){
if(dis[i][k]==inf)continue;
for(int j=1;j<=n;++j){
if(dis[k][j]<inf)
dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
}
struct que
{
int maxsize ;
STA q[1000000];
int front;
int rear;
int size;
que()
{
front = rear = 0;
size = 0;
maxsize = 999999;
}
void clear()
{
front = rear = 0;
size = 0;
}
void push(STA a)
{
q[rear] = a;
rear ++;
rear%=maxsize;
size ++;
}
STA pop()
{
STA ret = q[front];
front++;
front%= maxsize;
size--;
return ret;
}
bool empty()
{
return size == 0 ;
}
};
que q;
bool solve(int &money,int &h)
{
// queue<STA> q;
q.clear();
memset(in,0,sizeof(in));
memset(dp,-1,sizeof(dp));
q.push(STA(1,0));
dp[id[1]][0]=money;
in[id[1]][0]=1;
if(money>ci[1]){
dp[id[1]][1<<id[1]] = money - ci[1]+di[1];q.push(STA(1,1<<id[1]));
in[id[1]][1<<id[1]] = 1;
}
//++++++++++++++++++++++++++++参考http://blog.csdn.net/acm_cxlove/article/details/7963286
// for(int i = 0 ;i< (1<<h);++i){
// for(int j = 0 ;j<h;++j){
// if(dp[j][i]<0)continue;
// for(int k = 0;k<h;++k){
// if(k==j||((1<<k)&i)) continue;
// if(dp[j][i]>=dis[icity[j]][icity[k]]+ci[icity[k]])
// dp[k][i|(1<<k)]=max(dp[k][i|(1<<k)],dp[j][i]-dis[icity[j]][icity[k]]-ci[icity[k]]+di[icity[k]]);
// }
// }
// }
//
// bool ans=false;
// for(int i=0;i<h;i++)
// //最后判断能不能返回起点
// if(dp[i][(1<<h)-1]>=dis[icity[i]][1]){
// ans=true;
// break;
// }
// return ans?1:0;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
while(!q.empty()){
STA f = q.pop();
// q.pop();
int u = f.city;
in[id[u]][f.s]=0;
int st = f.s;
int idu = id[u];
for(int idv = 0; idv < h;++idv){
if(idv == idu)continue;
int v = icity[idv];
if(dis[u][v] == inf)continue;
if(dp[idu][st] < dis[u][v])continue;
int tst = (st | (1<<idv));
if(tst == st)continue;
if(dp[idu][st] < ci[v]+dis[u][v])continue;
if(dp[idv][tst] < dp[idu][st]-ci[v]-dis[u][v]+di[v]){
dp[idv][tst] = dp[idu][st]-ci[v]-dis[u][v]+di[v];
if(!in[idv][tst]){
q.push(STA(v,tst));in[idv][tst] = 1;
}
if(v == 1 && tst == (1<<h)-1)return true;
}
}
}
bool ans=false;
for(int i=0;i<h;i++)
if(dp[i][(1<<h)-1]>=dis[icity[i]][1]){
ans=true;
break;
}
return ans?1:0;
}
int main()
{
// freopen("data.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--){
int n,m,money;int h;
scanf("%d%d%d",&n,&m,&money);
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
dis[i][j]=inf;
}
}
for(int i=0;i<m;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(dis[u][v]>w){
dis[u][v] = w;
dis[v][u] = w;
}
}
scanf("%d",&h);
memset(id,-1,sizeof(id));
memset(ci,0,sizeof(ci));
memset(di,0,sizeof(di));
for(int i=0;i<h;++i){
int t,c,d;
scanf("%d%d%d",&t,&c,&d);
id[t]=i;
icity[i]=t;
di[t]=c;
ci[t]=d;
}
if(id[1]<0){
id[1]=h;
icity[h++]=1;
}
Floyd(n);
if(solve(money,h))puts("YES");
else puts("NO");
}
return 0;
}