首页 > 代码库 > hdu 3810 Magina 队列模拟0-1背包

hdu 3810 Magina 队列模拟0-1背包

题意:

出一些独立的陆地,每片陆地上有很多怪物,杀掉每个怪物都需要一定的时间,并能获得一定的金钱,给出指定的金钱m, 求最少要多少时间可以得到m金钱,仅能选择一个陆地进行杀怪。

题解:

这题,如果不管数据范围,很容易想到对每片陆地求一次0-1背包(dp(i, j) = min(dp(i-1, j), dp[i-1, j-money] + time), i 为金钱),然后在所有陆地中找出最少的时间即为答案,但是这题的数据范围太大金钱m可达到1e9, 所以不能单纯的直接用数组模拟,考虑第i个怪物,要更新第i个怪物,只需要知道两个状态,那就是dp[i-1][j], 和dp[i-1][j-money], 所以,我们只需要保存第i-1个怪物的所有合法状态,就能更新第i个怪物的状态, 所以我们可以考虑使用两个优先队列来维护上一轮的状态和这一轮的状态,就能找到答案了。首先,将两个队列都按照money从大到小,time从小到大的顺序排列,每次把q1全部出队列更新下一个状态,并把两个状态都放入q2中,然后从q2中选择最优解再复制到q1中,最后更新ans即可。

代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef vector<int>::iterator Pointer;
const int maxn = 60;
const int inf = 1e9;
vector<int> group[maxn];
vector<int> mp[maxn];
int n, m, gcnt;
struct node
{
    LL tm, val;
    node() {}
    node(LL tm, LL val) : tm(tm), val(val) {}
    bool operator < (const node& tmp) const
    {
        return val < tmp.val || (val == tmp.val && tm > tmp.tm);
    }
}arr[maxn], now, nxt;
void init()
{
    for (int i = 0; i < maxn; ++i) group[i].clear(), mp[i].clear();
}
bool vis[maxn];
void dfs(int u)
{
    group[gcnt].push_back(u);
    vis[u] = true;
    for (Pointer it = mp[u].begin(); it != mp[u].end(); ++it)
    {
        int v = *it;
        if (!vis[v])
        {
            dfs(v);
        }
    }
}
void divide_group()
{
    memset(vis, false, sizeof vis);
    gcnt = 0;
    for (int i = 1; i <= n; i++)
    {
        if (!vis[i])
        {
            dfs(i);
            gcnt++;
        }
    }
}

priority_queue<node> q1, q2;
inline void clear_queue()
{
    while (!q1.empty())
    {
        q1.pop();
    }
    while (!q2.empty())
    {
        q2.pop();
    }
}
LL mint;
void zeroOnePack(int team)
{
    for (Pointer it = group[team].begin(); it != group[team].end(); ++it)
    {
        int v = *it;
        while (!q1.empty())
        {
            now = q1.top(), q1.pop();
            q2.push(now);
            nxt = node(now.tm+arr[v].tm, now.val+arr[v].val);
            if (nxt.val >= m && nxt.tm < mint)
            {
                mint = nxt.tm; continue;
            }
            if (nxt.tm >= mint) continue;
            q2.push(nxt);
        }
        LL pre = inf;
        while (!q2.empty())
        {
            node tmp = q2.top(); q2.pop();
            if (tmp.tm < pre)
            {
                pre = tmp.tm;
                q1.push(tmp);
            }
        }
    }
}
LL solve()
{
    mint = inf;
    
    for (int i = 0; i < gcnt; i++)
    {
        now = node(0, 0);
        clear_queue();
        q1.push(now);
        zeroOnePack(i);
    }
    
    return mint == inf ? -1 : mint;
}
int main()
{
//    freopen("/Users/apple/Desktop/in.txt", "r", stdin);
    
    int t, kase = 0; scanf("%d", &t);
    
    while (t--)
    {
        scanf("%d%d", &n, &m);
        init();
        for (int i = 1; i <= n; i++)
        {
            int k; scanf("%lld%lld%d", &arr[i].tm, &arr[i].val, &k);
            for (int j = 0; j < k; j++)
            {
                int v; scanf("%d", &v);
                mp[i].push_back(v);
            }
        }
        divide_group();
        LL ans = solve();
        printf("Case %d: ", ++kase);
        if (ans == -1)
        {
            printf("Poor Magina, you can't save the world all the time!\n");
        }
        else
        {
            printf("%lld\n", ans);
        }
    }
    
    
    
    
    return 0;
}


hdu 3810 Magina 队列模拟0-1背包