动态规划专题 - 解题报告 - H

博客介绍了树形结构的构建及dp状态更新方法。eom选择和moe选择有不同的dp状态更新公式,需通过递归dfs构建树形结构。前置准备包括读入、前向星建单向图和用结构体构建dp数组,关键在于dfs写法,要根据选择方案更新儿子节点和当前节点dp值。

eom选择的时候:
dp[i]=make_pair(dp[j].eom+a[i],dp[j].moe)
j为节点i的儿子中dp[j].first尽可能大并在此基础上dp[j].second尽可能小的儿子编号。
moe选择的时候:
dp[i]=make_pair(dp[k].eom,dp[k].moe+a[i])
k为节点i的儿子中dp[k].first尽可能小并在此基础上dp[k].second尽可能大的儿子编号。

我们需要一个递归dfs的方式来构建出树形的结构,为此的前置准备有读入,前向星建单向图,并以结构体构建dp数组来保存状态。关键在于dfs的写法,
需要现在节点的位置now和选择的人who,在从now走图的返回结果中每一次都根据选择的方案更新选择的儿子节点,循环递归之后根据最优儿子节点更新当前节点的dp值。
(好像写起来并不难?)

#include<bits/stdc++.h>
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define maxn 2000005
#define maxm 55
#define hrdg 1000000007
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
using namespace std;

char s[4];
int n, u, v, type;
ll val[maxn];
struct Edge{int to, nex;}edge[maxn];
int head[maxn], tot;
struct node{
    ll moe, eom;
}dp[maxn];

inline int read(){
    char c=getchar();long long x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

void add_edge(int u, int v){
    tot++;
    edge[tot].nex = head[u];
    edge[tot].to = v;
    head[u] = tot;
}

bool cmp2(int x, int y){						//moe想要的排序规则
    if(x == 0)						//就是说当前的best==0,那么可以直接赋值
        return true;
    if(dp[y].eom > dp[x].eom)			
        return true;
    if(dp[x].eom == dp[y].eom)
        if(dp[y].moe <= dp[x].moe)
            return true;
    return false;
}
bool cmp1(int x, int y){					//eom想要的排序规则,后来我发现他们都是以eom的石子数量为第一标准,这两个规则可以合并的其实
    if(x == 0)
        return true;
    if(dp[y].eom < dp[x].eom)
        return true;
    if(dp[x].eom == dp[y].eom)
        if(dp[y].moe >= dp[x].moe)
            return true;
    return false;
}

void dfs(int now, int who)
{
    int best = 0;
    for (int i = head[now]; i; i = edge[i].nex)
    {
        int to = edge[i].to;
        dfs(to, !who);
        if(who == 1 && cmp1(best, to))			//两个排序规则,本来可以处理的更聪明些的,但是也没差其实,至少思路还算清晰
            best = to;
        if(who == 0 && cmp2(best, to))
            best = to;
    }
    if(who == 1)					//有best的选择了,那么就看是谁拿的,赋值更新当前节点即可
    {
        dp[now].eom = dp[best].eom + val[now];
        dp[now].moe = dp[best].moe;
    }
    else
    {
        dp[now].eom = dp[best].eom;
        dp[now].moe = dp[best].moe + val[now];
    }
}

int main()
{
    scanf("%s", s);
    if(!strcmp(s, "eom"))
        type = 1;
    else
        type = 0;
    //printf("type = %d\n", type);
    n = read();
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++)
        val[i] = read();					//初始化和读入
    for (int i = 1; i < n; i++)
    {
        u = read(); v = read();
        add_edge(u, v);				//前向星建图,有向边
    }
    dfs(1, type);							//递归深搜
    printf("%lld %lld", dp[1].eom, dp[1].moe);				//打印结果
    return 0;
}

/*
moe
6
1 3 5 3 4 2
1 2
1 3
3 4
3 5
5 6
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值