hdu Disney's FastPass(状态压缩dp)

来源:互联网 发布:手机淘宝账号在哪里 编辑:程序博客网 时间:2024/05/22 01:13

这种题我一直喜欢用bfs搞的,但是这个题不太好bfs,主要是我刚开始想的是通过边进行状态转移,这样很不好写。。。于是就坑爹了,调了很久sample都没出。。。

于是学习了一下别人的思想。。。通过“目的”来进行状态转移而不是边。当你在某个点的时候,你可以为了游历某个景点去某个点,也可以为了获取某个点的票而去某个点。能想到这一点的话状态转移方程就很好写了,但是也要能想到啊。。。

ps:注意代码中的(1) 跟(2)的关系。

#include<algorithm>#include<iostream>#include<cstring>#include<fstream>#include<sstream>#include<vector>#include<string>#include<cstdio>#include<bitset>#include<queue>#include<stack>#include<cmath>#include<map>#include<set>#define FF(i, a, b) for(int i=a; i<b; i++)#define FD(i, a, b) for(int i=a; i>=b; i--)#define REP(i, n) for(int i=0; i<n; i++)#define CLR(a, b) memset(a, b, sizeof(a))#define debug puts("**debug**")#define LL long long#define PB push_back#define MP make_pair#define eps 1e-8using namespace std;const int maxn = 55;const int INF = 1e9;int T, n, m, k, tot, u, v, w, ni;int dp[1<<8][1<<8][maxn], g[maxn][maxn], p[10], t[10], ft[10];LL in[10]; //in[i]:第i个景点的票在那些点能拿到inline int bit(int x) { return 1<<x; }inline void CheckMin(int &a, int b) { if(a == -1 || a > b) a = b; }//x点能拿到那些景点的票inline int has(int x){    int ret = 0;    REP(i, k) if(in[i]&(1LL<<x)) ret |= 1<<i;    return ret;}void pre(){    scanf("%d%d%d", &n, &m, &k);    CLR(g, -1); CLR(in, 0);    REP(i, m)    {        scanf("%d%d%d", &u, &v, &w);        CheckMin(g[u][v], w);        g[v][u] = g[u][v];    }    REP(i, k)    {        scanf("%d%d%d%d", &p[i], &t[i], &ft[i], &ni);        while(ni--)        {            scanf("%d", &u);            in[i] |= 1LL<<u;        }    }}void floyd(){    FF(k, 1, n+1)        FF(i, 1, n+1) if(i != k && g[i][k] != -1)            FF(j, 1, n+1) if(k != j && g[k][j] != -1)            {                if(i == j) g[i][j] = 0;                else CheckMin(g[i][j], g[i][k] + g[k][j]);            }}int solve(){    //dp[vis][have][i]:    //已经游历过vis状态的点,拥有have状态的票 目前在i城市的最小耗时    CLR(dp, -1);    dp[0][0][1] = 0;    tot = 1<<k;    REP(vis, tot)   REP(have, tot) if((vis&have) == vis) //vis是have的真子集......(1)    FF(i, 1, n+1) if(dp[vis][have][i] != -1)    {        REP(j, k) if((vis & bit(j)) == 0)//未游离j景点        {             //去游历j景点 获得该点的所有票             //游历j景点不一定需要票 但游历后默认为有j票  ......(2)            int sta = has(p[j]);            CheckMin(dp[vis|bit(j)][have|sta|bit(j)][p[j]], dp[vis][have][i] + g[i][p[j]] + (((have|sta)&bit(j)) ? ft[j] : t[j]));        }        FF(j, 1, n+1)        {            //去某点获得该点的所有票            int sta = has(j);            if(sta == have) continue;            if((sta&have) != sta)                CheckMin(dp[vis][have|sta][j], dp[vis][have][i] + g[i][j]);        }    }    int ret = INF;    REP(have, tot) FF(i, 1, n+1) if(dp[tot-1][have][i] != -1)        CheckMin(ret, dp[tot-1][have][i] + g[i][1]);    return ret;}int main(){   scanf("%d", &T);   FF(kase, 1, T+1)   {       pre();       floyd();       printf("Case #%d: %d\n", kase, solve());   }   return 0;}