[CTSC2010]星际旅行(带反悔的贪心)

Posted chtholly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CTSC2010]星际旅行(带反悔的贪心)相关的知识,希望对你有一定的参考价值。

题目

有一棵树,限制从每个点出发的次数最多为(c_i),对于每个点i,求1到i的路径最多经过多少(,(nleq 40000)),保证每个点的(c)大于其入度

解法1

直接莽?拆点,中间连容量为(c_i)费用为0的边,点与点之间连容量为inf费用为1的边,从1到每个点i跑一次最大费用最大流即可

显然过不了本题数据

解法2

考虑树形DP解决这个网络流问题

递推解决问题?本题难点在于:确定递推顺序然后确定一个点的答案

由于每个点的(c)都大于其入度,那么从1开始一定可以将所有点走一遍回到1,先走这样的一条路;
然后如果一条边两端的点都还可以走,那么走一个环可以再多给答案+2,直到每条边两端都至少有1个点(c)值为0为止,容易发现这样走小圈的贪心对于(ans_1)是最优的

根节点的答案求出来了,于是考虑从父亲向儿子递推:

  1. 父亲的(c)值不为0,由父亲走到儿子,那么儿子的答案为父亲+1;(由于父亲最优所以儿子也不存在更优解)

  2. 父亲的(c)值为0,由于父亲的答案为终止在父亲节点的路径,那么这条路径不可能再回到儿子节点了,所以不能走这条儿子->父亲的边,将这条边退掉,儿子又有一次走的机会;此时如果儿子的儿子还可以走那就走一个小环,否则就不加

注意,由于上面的贪心已经使得一条边的两端至少有1个点(c)值为0,所以上面的分类讨论中,即使可以多走也只是1~2条边

Code

#include<bits/stdc++.h>
#define N 50005
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
int n,c[N],ans[N],g[N],now;
struct Edge
{
    int next,to;
}edge[N<<1];int head[N],cnt=1;
void add_edge(int from,int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}

template <class T>
void read(T &x)
{
    char c; int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}

void DFS(int rt,int fa)
{
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        DFS(v,rt);
        int ct=Min(c[rt],c[v]);
        c[rt]-=ct; c[v]-=ct;
        now+=ct*2;
        if(c[v]) g[rt]=v;
    }
}
void dfs(int rt,int fa)
{
    ans[rt]=now;
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        int flag=0;//回溯用 
        if(c[rt]>0) { --c[rt]; ++now; flag=1; }
        else if(g[v]) { --c[g[v]]; ++now; flag=2; }
        else { ++c[v]; --now; flag=3; }
        dfs(v,rt);
        if(flag==1) ++c[rt],--now;
        if(flag==2) ++c[g[v]],--now;
        if(flag==3) --c[v],++now;
    }
}
int main()
{
    read(n);
    for(int i=1;i<=n;++i) read(c[i]);
    for(int i=1;i<n;++i)
    {
        int x,y;
        read(x);read(y);
        ++x;++y;
        add_edge(x,y);
        add_edge(y,x);
        --c[x];--c[y];
        now+=2; 
    }
    DFS(1,0); dfs(1,0);
    for(int i=1;i<=n;++i) printf("%d
",ans[i]);
    return 0;
}

以上是关于[CTSC2010]星际旅行(带反悔的贪心)的主要内容,如果未能解决你的问题,请参考以下文章

[每日一题]:P1016 旅行家的预算 -- 反悔贪心

题解CTST2010星际旅行

反悔贪心

[CTSC2007]数据备份Backup 题解

bzoj 1150: [CTSC2007]数据备份Backup链表+堆

[CTSC1999][网络流24题] 星际转移