码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 3810 Magina 搜索+队列模拟分组背包

时间:2016-05-13 04:11:34      阅读:112      评论:0      收藏:0      [点我收藏+]

标签:

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

题意:有n个地方有怪,杀死怪消耗时间得到金钱。在某些怪处可以瞬移到另一些怪处(双向),不花费时间。求取得金钱M的最小时间。


首先,这是一张无向图,可以看成若干个连通分量。敌法可以一个连通分量内任意移动,因为不消耗时间。

然后这题就变成了一个分组背包的问题。每个组内是一个背包问题,对于一个怪,你可以选择杀或不杀。如果数据正常,这题就可以A过了。

然而这题的数据不正常。背包的容量最大可以达到10亿,用普通的背包来做肯定不行。又发现怪的数量最多只有50。那么可以考虑用队列来模拟背包的过程,类似于离散化。

保存杀或者不杀怪的状态。其细节可以用滚动数组来实现,一个保存当前状态,一个保存更新后的状态。但这样最大的状态数依然有2^50那么多,依然不行。

背包九讲中讲过一个简单有效的优化 

一个简单有效的优化
完全背包问题有一个很简单有效的优化,是这样的:若两件物品ij满足c[i]<=c[j]w[i]>=w[j],则将物品j去掉,不用考虑。这个优化的正确性显然:任何情况下都可将价值小费用高得j换成物美价廉的i,得到至少不会更差的方案。对于随机生成的数据,这个方法往往会大大减少物品的件数,从而加快速度。然而这个并不能改善最坏情况的复杂度,因为有可能特别设计的数据可以一件物品也去不掉。

我们可以用优先队列来进行剪枝,将时间多且金钱少的状态通通减掉。
这题最关键的一点还是用队列来模拟背包问题。一些优化的思路也很经典。


#include <iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#define INF 0x7ffffff
#define N 55

using namespace std;

struct node
{
    int c,w;
}p[N];

vector<node> group[N];

struct Node
{
    int c,w;//c 时间,w 金钱
    bool operator<(const Node a)const
    {
        return w!=a.w?w<a.w:c>a.c;  //金钱从大到小,时间从小到大排列;
    }
}s;

int ans,cnt,v[N],n,m,mp[N][N];

void dfs(int c)
{
    v[c]=1;
    group[cnt].push_back(p[c]);
    for(int i=1;i<=n;i++)
        if(!v[i]&&mp[c][i])
            dfs(i);
}

void divid()//分组
{
    cnt=0;
    memset(v,0,sizeof(v));
    for(int i=1;i<=n;i++)
        if(!v[i])
        {
            group[cnt].clear();
            dfs(i);
            cnt++;
        }
}

void slove()
{
    ans=INF;
    priority_queue<Node> q1,q2;
    for(int i=0;i<cnt;i++)//对每组做背包
    {
        while(!q1.empty())  q1.pop();
        while(!q2.empty())  q2.pop();
        s.c=s.w=0;
        q1.push(s);
        for(int j=0;j<group[i].size();j++)
        {
            while(!q1.empty())//对每个状态都考虑第j个物品取不取
            {
                s=q1.top();
                q1.pop();
                q2.push(s);
                s.c+=group[i][j].c;
                s.w+=group[i][j].w;
                if(s.w>=m)//剪枝
                {
                    ans=min(ans,s.c);
                    continue;
                }
                if(s.c>=ans)    continue;//剪枝
                q2.push(s);
            }
            int mincost=INF;
            while(!q2.empty())// 滚动数组
            {
                s=q2.top();
                q2.pop();
                if(s.c<mincost)//更新状态+剪枝,将时间长且金钱少的状态去掉。
                    q1.push(s),mincost=s.c;
            }
        }
    }
}

int main()
{
    int T,kase=0;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        memset(mp,0,sizeof(mp));
        for(int i=1;i<=n;i++)
        {
            int k;
            scanf("%d%d%d",&p[i].c,&p[i].w,&k);
            while(k--)
            {
                int t;
                scanf("%d",&t);
                mp[i][t]=mp[t][i]=1;
            }
        }
        divid();
        slove();
        printf("Case %d: ",++kase);
        if(ans==INF)    printf("Poor Magina, you can't save the world all the time!\n");
            else    printf("%d\n",ans);
    }
}


hdu 3810 Magina 搜索+队列模拟分组背包

标签:

原文地址:http://blog.csdn.net/zchahaha/article/details/51336423

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!