HDU 4114 Disney's FastPass(状压dp)

题意就是给你一些点,有的地方有景区,有的地方有某些景点的优先票,拿优先票去景点和不拿的时间不一样。问从地点1出发去所有景点再回来的最短时间。

得把景点,景区进行状压。即dp[i][j][k]代表参观景点状态为i,景区状态为j,停在k的最短时间。

先floyd一下处理出最短路。

对于一种状态i,j,为了保证dp[i][j][k]在递推的时候是最短时间可以先用2个(1-n)for循环进行更新,因为可能有的位置的状态是-1即不存在。事实上只要有一个点存在i与j的状态,那么其他点必定都存在i,j这个状态。

然后再去更新去看景点的情况,在做过刚才那个处理后,其实只需要看当前位置下的景点就行,看网上其他人的代码则是遍历去看所有景点,如果这样做的话就不用先做那个处理了,可以放在一起做。

另外,还有一个优化,如果已经参观了那个节点,那么就强行给他一个票,其实就是在其他条件都相同的情况下(在同一个点,经过同样的景点),有票跟没票的比较。这样的话,参观景点的集合就是票的集合的子集了。然后又容易看出其实i跟j的递推顺序交换也是一样的,所以就用了枚举子集的办法,然后把子集从小到大推。

竟然刷到rank1了。577ms。

AC代码:

#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<ctime>
#include<string.h>
#include<string>
#include<sstream>
#include<bitset>
using namespace std;
#define ll __int64
#define ull unsigned long long
#define eps 1e-8
#define NMAX 1<<29
#define MOD 1000000
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI acos(-1)
template<class T>
inline void scan_d(T &ret)
{
    char c;
    int flag = 0;
    ret=0;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c == '-')
    {
        flag = 1;
        c = getchar();
    }
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
    if(flag) ret = -ret;
}

int dp[1<<8][1<<8][55],dist[55][55];

struct node
{
    int t,ft;
};
node no[10];

inline void update(int &a, int b)
{
    if(a < 0 || a > b)
        a = b;
}

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++)
                dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]);
}

int pos[10],getft[55];
int s[1<<8];
int main()
{
#ifdef GLQ
    freopen("input.txt","r",stdin);
//    freopen("o.txt","w",stdout);
#endif
    int t,n,m,K,cas = 1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&K);
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
                dist[i][j] = NMAX;
            dist[i][i] = 0;
        }
        for(int i = 1; i <= m; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            dist[u][v] = dist[v][u] = w;
        }
        floyd(n);
        memset(getft,0,sizeof(getft));
        for(int i = 0; i < K; i++)
        {
            int tmp;
            scanf("%d%d%d%d",&pos[i],&no[i].t,&no[i].ft,&tmp);
            while(tmp--)
            {
                int t1;
                scanf("%d",&t1);
                getft[t1] |= (1<<i);
            }
        }
        for(int i = 0; i < (1<<K); i++)
            for(int j = 0; j < (1<<K); j++)
                for(int k = 1; k <= n; k++) dp[i][j][k] = -1;
        dp[0][0][1] = 0;
        for(int j = 0; j < (1<<K); j++)
        {
            int nct = 0;
            for(int i = j; i ; i = (i-1)&j) s[nct++] = i;
            s[nct++] = 0;
            for(int w = nct-1; w >= 0; w--)
            {
                int i = s[w];
                for(int k = 1; k <= n; k++) if(dp[i][j][k] >= 0)
                {
                    for(int l = 0; l < K; l++) if(!(i&(1<<l)))
                    {
                        int cost = dist[k][pos[l]];
                        if(j&(1<<l)) cost += no[l].ft;
                        else cost += no[l].t;
                        update(dp[i|(1<<l)][j|getft[pos[l]]|(1<<l)][pos[l]],dp[i][j][k]+cost);
                    }
                    for(int l = 1; l <= n; l++)
                        update(dp[i][j|getft[l]][l],dp[i][j][k]+dist[k][l]);
                }
//                for(int k = 1; k <= n; k++) if(dp[i][j][k] >= 0)//这种递推就可以只看自己点上面的景点
//                {
//                    for(int l = 1; l <= n; l++)
//                        update(dp[i][j|getft[l]][l],dp[i][j][k]+dist[k][l]);
//                }
//                for(int k = 1; k <= n; k++) if(dp[i][j][k] >= 0)
//                {
//                    for(int l = 0; l < K; l++) if(!(i&(1<<l)))
//                    {
//                        if(pos[l] != k) continue;
//                        int cost;
//                        if(j&(1<<l)) cost = no[l].ft;
//                        else cost = no[l].t;
//                        update(dp[i|(1<<l)][j|getft[pos[l]]|(1<<l)][pos[l]],dp[i][j][k]+cost);
//                    }
//                }
            }
        }
        printf("Case #%d: %d\n",cas++,dp[(1<<K)-1][(1<<K)-1][1]);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值