hdu 4284——Travel

48 篇文章 0 订阅

题意:一个国家有最多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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值