HDU 3810 Magina

13 篇文章 0 订阅


直接贴上ZeroClock的分析吧。。。


题目大意题目源自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),那肯定是前面的更优,对吧?就是这样。


Code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define LL long long
using namespace std;

const int maxn=55;
const int inf=1000000000;
class node{
 public:
    int val,t;
    node(int x=0,int y=0){t=x;val=y;}
    friend bool operator<(node a,node b){
        if(a.val==b.val) return a.t>b.t;
        return a.val<b.val;
    }
}arry[maxn],now,next;
bool maze[maxn][maxn],visit[maxn];
int group[maxn][maxn],num[maxn];
int n,m,totg;
priority_queue<node>qu1,qu2;

void DFS(int pos){
    visit[pos]=true;
    group[totg][++num[totg]]=pos;
    for(int i=1;i<=n;i++)
        if(!visit[i]&&maze[pos][i]) DFS(i);
}

void DivideGroup(){
    memset(num,0,sizeof(num));
    memset(visit,false,sizeof(visit));
    totg=1;
    for(int i=1;i<=n;i++)
        if(!visit[i]) DFS(i),totg++;
}

LL Solve(){
    int mint=inf;
    for(int i=1;i<totg;i++){
        while(!qu1.empty()) qu1.pop();
        while(!qu2.empty()) qu2.pop();
        qu1.push(node(0,0));
        for(int j=1;j<=num[i];j++){
            int pos=group[i][j];
            while(!qu1.empty()){
                now=qu1.top(); qu1.pop();
                qu2.push(now);
                next=now;

                next.t+=arry[pos].t;
                next.val+=arry[pos].val;
                if(next.val>=m){
                    if(next.t<mint) mint=next.t;
                    continue;
                }
                if(next.t>=mint) continue;
                qu2.push(next);
            }
            int tmp=inf;
            while(!qu2.empty()){
                now=qu2.top(); qu2.pop();
                if(now.t<=tmp) qu1.push(now),tmp=now.t;
            }
        }
    }
    return mint==inf?-1:mint;
}

int main()
{
    int T,cas=1;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        memset(maze,false,sizeof(maze));
        for(int i=1;i<=n;i++){
            int k,u;
            scanf("%d %d %d",&arry[i].t,&arry[i].val,&k);
            for(int j=1;j<=k;j++){
                scanf("%d",&u);
                maze[i][u]=maze[u][i]=true;
            }
        }
        DivideGroup();
        LL ans=Solve();
        printf("Case %d: ",cas++);
        if(ans==-1) printf("Poor Magina, you can't save the world all the time!\n");
        else        printf("%d\n",ans);
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值