Hdu 3810 Magina (DP_背包)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3810


题目大意题目源自Dota,前面一堆介绍敌法师,最后一段才开始说有n堆野兽,每堆野兽屠杀完完需要花费ti时间,可以增加金钱gi,敌法师有瞬移技能,可以从某堆野兽移到另一堆野兽,题目有给定从哪堆可以移到哪堆。最后问在满足打的金钱多余m的情况下的最少时间。


解题思路2011年武汉邀请赛网络赛的金牌题,题目看上去很复杂,又是n堆野兽,又是各种瞬移。但是理清楚之后就发现这是题背包题,先把能够相连的野兽堆用深搜找出来归为一组,然后我们要做的就是在这组里面选择0,1,...num[tot]堆进行屠杀,每堆野兽有两种选择:杀获不杀.这样问题就转为求tot组01背包。

     但本题的金钱和屠杀时间范围都特别大,屠杀时间的范围是1-1000万,金钱的范围是1-10亿,不能用常规的两重循环模拟。其实用两个优先队列就可以模拟01背包的计算过程,第一次将(0,0)入队,然后出队转移到下一个状态,转移完这两个状态都进入队列,可以用两个优先队列进行操作,一个表示上一轮的计算结果,一个是这一轮,就相当于滚动数组。在用优先队列模拟的过程中要注意剪枝,如果不剪或者剪得不好就是MLE。我试了好几种剪枝,只有一种可以,那就是在每次转移完要把队列1复制到队列2的时候,判断下入队的这个点是不是满足价值比前面入队的点金钱少且耗时少,如果不是,这个肯定比如前面入队的那个优。比如,(6,4)表示打到钱为6,且耗时4,如果下一个要进队的是(5,5),那肯定是前面的更优,对吧?就是这样。

    这题还因为输出不可能的情况时少写了++Cas导致Wa了几次。由于前面一直MLE,后面Wa,认为是算法的问题,一直检查算法。我想这种情况在现场赛也可能碰到,现在碰到了是好事,小心驶得万年船,谨以为戒。

   按我这样写比iSea的标程快了15倍,第一榜第一位,剪得够强够有力,得瑟下。    


测试数据:

10
3 9
3 3 2
2 3
3 3 2
1 3
3 3 2
1 2


3 3
3 3 2
2 3
5 3 2
1 3
4 3 2
1 2

1 4
2 5 0

1 5
1 4 0

4 10
1 9 0
3 3 1
3
3 3 2
2 4
4 4 1
3


代码:

#include <stdio.h>
#include <string.h>
#include <queue>
#include <string.h>
using namespace std;
#define MIN 52
#define INF 1000000000
#define int64 __int64


struct node {

    int64 t,val;
    friend int operator < (node a,node b) {

		if (a.val == b.val)
			return a.t > b.t;
		else
			return a.val < b.val;
    }
}arr[MIN],now,next;
priority_queue<node> qu1,qu2;
int n,m,maze[MIN][MIN],vis[MIN];
int gtot,num[MIN],group[MIN][MIN];


void Dfs(int i) {

    vis[i] = 1;
    int j = ++num[gtot];
    group[gtot][j] = i;
    for (int k = 1; k <= n; ++k)
        if (!vis[k] && maze[i][k]) Dfs(k);
}
void DivideGroup() {

    int i,j,k;
    for (gtot = i = 1; i <= n; ++i)
        if (!vis[i]) Dfs(i),gtot++;
}
int64 Solve_1A() {

    int i,j,k;
    int64 minn,tpk,tpval;
    minn = INF;        //时间最多为1000万 * 50 == 5亿,用10亿够大了


    for (i = 1; i < gtot; ++i) {
    //模拟01背包
        while (!qu1.empty()) qu1.pop();
        while (!qu2.empty()) qu2.pop();
        now.t = now.val = 0;
        qu1.push(now);


        for (j = 1; j <= num[i]; ++j) {

            k = group[i][j];        //group存的是第几堆野兽
            while (!qu1.empty()) {

                now = qu1.top();
				qu1.pop(),qu2.push(now);
				next.t = now.t + arr[k].t;
                next.val = now.val + arr[k].val;
                   

                if (next.val >= m) {

                    if (next.t < minn) minn = next.t;
                    continue;
                }
				if (next.t >= minn) continue;
				qu2.push(next);
            }


			tpk = INF;
            while (!qu2.empty()) {
					
                now = qu2.top(),qu2.pop();
                if (tpk >= now.t) qu1.push(now),tpk = now.t;//前面的val大时间也要大,如果后面出队的时间更多,那就要剪掉
            }
        }
    }
    return minn == INF ? -1 : minn;
}


int main()
{
    int i,j,t,cas = 0;
    int u,v,k;


    scanf("%d",&t);
    while (t--) {

        scanf("%d%d",&n,&m);
        memset(maze,0,sizeof(maze));
        memset(vis,0,sizeof(vis));
        memset(num,0,sizeof(num));
        for (i = 1; i <= n; ++i) {

            scanf("%d%d%d",&arr[i].t,&arr[i].val,&k);
            while (k--) {

                scanf("%d",&u);
                maze[i][u] = 1;
            }
        }


        DivideGroup();   //能够相互连接的算一堆
        int64 ans = Solve_1A();
        if (ans != -1) printf("Case %d: %I64d\n",++cas,ans);
        else printf("Case %d: Poor Magina, you can't save the world all the time!\n",++cas);
    }
}

文ZeroClock原创,但可以转载,因为我们是兄弟。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值