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

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

时间:2019-11-07 09:44:36      阅读:92      评论:0      收藏:0      [点我收藏+]

标签:开始   题目   span   namespace   存在   oid   max   dfs   路径   

题目

有一棵树,限制从每个点出发的次数最多为\(c_i\),对于每个点i,求1到i的路径最多经过多少\(,(n\leq 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\n",ans[i]);
    return 0;
}

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

标签:开始   题目   span   namespace   存在   oid   max   dfs   路径   

原文地址:https://www.cnblogs.com/Chtholly/p/11809811.html

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