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

 

posted on 2012-09-14 16:50  夜->  阅读(138)  评论(0编辑  收藏  举报