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

这种题我一直喜欢用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-8
using 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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值