HDU-6326 Problem H. Monster Hunter(贪心模拟)

题目:给定一棵 n 个点的树,除 1 外每个点有一只怪兽,打败它需要先消耗 ai点 HP,再恢复 bi点 HP。求从 1 号点出发按照最优策略打败所有怪兽一开始所需的最少 HP。

思路:以 1 为根将树转化成有根树,那么每只怪兽要在父亲怪兽被击败后才能被击败。假如没有父亲的限制,会产生一个最优的攻击顺序:

第一步:将怪兽分成两类:a < b 的和 a ≥ b 的,前一类打完会加血,后一类打完会扣血,显然最优策略下应该先打第一类再打第二类。对于 a < b 的怪兽,显然最优策略下应该按照 a 从小到大打。

第二步对于 a ≥ b 的怪兽,考虑两只怪兽 i,j,先打 i 再打 j 的过程中血量会减少到 HP + min(−ai,−ai+ bi− aj),因为 a ≥ b,所以这等于 HP−ai−aj+bi。同理先打 j 再打 i 的过程中血量会减少到 HP − ai− aj+ bj。可以发现按照任何顺序都只和 b 有关,最优策略下需要让血量尽可能多,因此要按照 b 从大到小打。

第三步:加上父亲的限制,根据之前的优先级将子节点与父节点合并,即打完父亲后立即打这个子节点是最优的。

具体实现起来就是用优先队列来贪心了,中间合并时用并查集。   最开始写完代码,怎么改都是很正确,然而一交就wa,就很烦,拿数据本地跑发现怎么就第一组数据正确???,后面的都是一个数!!!然后发现竟然是0号节点没有初始化(代码风格不同吧,最后我的代码用的有0这一节点,答案存在了0这个点上)。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200005;
struct node{
    int id,ti;
    ll a,b;
    bool operator < (const node &o)const
    {
        int d1=o.a<o.b,d2=a<b;
        if(d1!=d2) return d1>d2;//第一步分类
        if(d1>0) return o.a<a;
        return o.b>b;//第二步分类
    }
};
priority_queue<node>q;
node a[maxn];
int n,m,t;
vector<int>g[maxn];
int fa[maxn],f[maxn],vis[maxn];

int init()
{
    while(!q.empty())q.pop();
    for(int i=0;i<=n;i++)
    {
        g[i].clear();
        a[i].a=a[i].b=0;
        a[i].id=a[i].ti=0;
        fa[i]=0;
        f[i]=i;
        vis[i]=0;
    }
}
void dfs(int u,int pre)
{
    fa[u]=pre;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==pre) continue;
        dfs(v,u);
    }
}
int getfa(int x)
{
    return f[x]==x?x:f[x]=getfa(f[x]);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init();
        for(int i=2;i<=n;i++)
        {
            scanf("%lld%lld",&a[i].a,&a[i].b);
            a[i].id=i;
            a[i].ti=0;
            q.push(a[i]);
        }
        for(int i=1;i<n;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs(1,0);
        int tim=0,x;
        node t,tt;
        while(!q.empty())
        {
            t=q.top();
            q.pop();
            x=t.id;
            if(vis[x]!=t.ti||x==0)continue;
            int y=getfa(fa[x]);
            f[x]=y;
            vis[y]=++tim;
            a[y].a+=max(0ll,-a[y].b+a[x].a);//合并后新的a
            a[y].b=a[x].b+max(0ll,a[y].b-a[x].a);//合并后新的b
            tt.a=a[y].a;
            tt.b=a[y].b;
            tt.ti=vis[y];
            tt.id=y;
            q.push(tt);
       }
        printf("%lld\n",a[0].a);
    }
    return 0;
}
/*

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

1
4
2 0
5 0
6 2
1 4
4 2
4 3
*/

猜你喜欢

转载自blog.csdn.net/dllpXFire/article/details/81326164