[Ctsc2010]星际旅行(退流思想)

传送门

思想很重要,题目给出了每个星球的Hi大于等于与该星球直接相连的星球数(即度数)。

就说明从0点出发,一定可以再回到0点。

在第一次dfs中把一些还可以加的流加入使得答案更大。因为图此时已经连通。

然后对于相邻的两个点,就一定至少有一个的度数为0。

在第二次dfs中运用“退流”思想,对于每一个点求出它的ans,即如果最终旅行结束在这个星球,最多可以使用时空隧道的次数。

(摘自这儿

#include<bits/stdc++.h>
#define N 50003
using namespace std;
int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct EDGE{
    int nextt,to;
}w[N*2];
int tot=0,sum=0;
int h[N],head[N],r[N],ans[N];
void add(int a,int b)
{
    tot++;
    w[tot].nextt=head[a];
    w[tot].to=b;
    head[a]=tot;
}
void dfs1(int x,int fa)//在已经确定了可以走一个来回后加入多的流 
{
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(v==fa)continue;
        dfs1(v,x);
        int k=min(h[x],h[v]);
        sum+=k*2;
        h[x]-=k;h[v]-=k; 
        if(h[v])r[x]=v;//表示x这个点有退自己父亲的流的机会
    }
}
void dfs2(int x,int fa)//对于每一个点求ans 
{
    ans[x]=sum;
    int flagg;
    for(int i=head[x];i;i=w[i].nextt)
    {
        int v=w[i].to;
        if(v==fa)continue;
        if(h[x])flagg=1,h[x]--,sum++;
        else if(r[v])flagg=2,h[r[v]]--,sum++;
        else flagg=3,sum--,h[v]++;//退了到父亲的流 
        dfs2(v,x);
        if(flagg==1)h[x]++,sum--;
        else if(flagg==2)h[r[v]]++,sum--;
        else sum++,h[v]--;
    } 
}
int main() 
{
    int n=read();
    for(int i=1;i<=n;++i)
      h[i]=read();
    for(int i=1;i<n;++i)//一定要遍历所有的边一个来回,然后可以证明最优情况肯定是遍历的所有边的 
    {
        int a=read()+1,b=read()+1;
        add(a,b);add(b,a);sum+=2; 
        h[a]--;h[b]--;
    }
    dfs1(1,1);
    dfs2(1,1);
    for(int i=1;i<=n;++i)
      printf("%d\n",ans[i]);
}
View Code
posted @ 2019-08-25 21:49  yyys  阅读(274)  评论(0编辑  收藏  举报