[NOIP2017模拟]鸭舌

题目:
小美喜欢吃鸭舌。
有一个 n 个点的树,每个节点 i ,第 i 个点上有 ai 个鸭舌。
小美一开始处于 x 号点。
每次小美可以选择一个与现在的点有边的点而且那个点还有鸭舌,那么小美会走到那个点并吃一个鸭舌。
要保证小美最后还是走到 x 号点。
问小美最多能吃几个鸭舌?

输入格式
输入第一行一个整数 n 。
接下来一行 n 个整数表示 ai
下面是 n-1 行每行两个整数表示一条边。
最后一行一个整数表示 x 。

输出格式
输出一行,一个整数,表示吃最多的鸭舌个数。

样例数据 1
输入
5
1 3 1 3 2
2 5
3 4
4 5
1 5
4
输出
6
样例数据2
输入
3
2 1 1
3 2
1 2
3
输出
2

备注
【数据规模】
对于 30% 的数据:n≤5; ai ≤5;
对于 100% 的数据:1≤n≤100000;0≤ ai 109 ;1≤x≤n。

分析:刚开始想到应该dp,但是逻辑总是出错,花出大把时间,最后时间不够了想打暴力,也写错了……思路主要是对于一个节点,找到它的所有儿子,每一个儿子又有自己对应的子树,就这样深搜到底。

代码:
30分暴搜(就是一个老鼠走迷宫,我代码能力太弱啦,居然打错):

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=100005;
long long a[N];
int n,x;
int first[N],next[N*2],go[N*2],tot=0;
long long ans=0;
inline void comb(int a,int b)
{
  next[++tot]=first[a],first[a]=tot,go[tot]=b;
  next[++tot]=first[b],first[b]=tot,go[tot]=a;
}
inline void dfs(int now,long long tot)
{
  if(tot!=0)
    a[now]--;
  if(now==x)
    ans=max(ans,tot);
  for(int e=first[now];e;e=next[e])
  {
    int v=go[e];
    if(a[v]==0)  continue;
      dfs(v,tot+1);
  }

  a[now]++;
}
int main()
{
  //freopen("tongue.in","r",stdin);
  //freopen("tongue.out","w",stdout);
  scanf("%d",&n);
  if(n<=5)
  {  
    for(int i=1;i<=n;i++)
      cin>>a[i];
    for(int i=1;i<n;i++)
    {
      int a,b;
      scanf("%d%d",&a,&b);
      comb(a,b);
    }
    scanf("%d",&x);
    dfs(x,0);
    cout<<ans<<endl;
  }
  return 0;
}

100分代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=2e5+5;
int n,tot,root,len,a[N],size[N];
int first[N],next[N],to[N];
long long f[N],cnt[N],tmp[N];

void add(int x,int y)
{
    next[++tot]=first[x],first[x]=tot,to[tot]=y;
}

bool cmp(const long long &a,const long long &b)
{
    return a>b;
}

void dfs(int x,int fa)
{
    for(int i=1;i<=len;i++)tmp[i]=0;//用来记录所有儿子子树能吃到的鸭舌
    len=0;
    for(int e=first[x];e;e=next[e])//枚举每一条边
    {
        int y=to[e];
        if(a[y]&&y!=fa)
        {
            size[x]++;//记录有多少个儿子
            a[y]--;//去了这条边,先吃掉一个鸭舌
            if(a[y])dfs(y,x);//如果这个儿子还有鸭舌就继续搜索这个子树
            cnt[x]+=a[y];//记录子树走完后这个儿子还剩多少鸭舌
            tmp[++len]=f[y];//f记录走完这个子树能吃掉多少鸭舌,tmp是个临时数组,后面排序用的
            f[x]+=f[y];//先把子树吃掉的鸭舌加到f[父亲]
        }
    }
    if(a[x]>=size[x])//如果a[x]>size[x],说明可以把每个子树都走一遍
    {
        f[x]+=2*size[x];//把走到每个需要消耗的鸭舌加进来
        a[x]-=size[x];//a[x]失去size[x]个鸭舌
        f[x]+=2*min(cnt[x],(long long)a[x]);//再加上min(父亲剩下的,所有儿子剩下的),也就是最后在父亲和儿子之间不断来回走把其中一个吃完的过程
        a[x]-=min(cnt[x],(long long)a[x]);//父亲又少了这么多鸭舌
    }
    else//如果父亲的鸭舌量不够走完所有子树
    {
        f[x]=2*a[x];//父亲的肯定要消耗完,因为来回,所以*2(这里f[x]已经重新赋值了,所以前面那个f[x]+=f[y]不会出bug)
        sort(tmp+1,tmp+len+1,cmp);//从大到小排所有子树能吃掉的
        for(int i=1;i<=a[x];i++)
            f[x]+=tmp[i];//加上最多的那几个子树
        a[x]=0;//父亲清零
    }   
}

int main()
{
    freopen("tongue.in","r",stdin);
    freopen("tongue.out","w",stdout);
    int x,y;
    n=getint();
    for(int i=1;i<=n;i++)a[i]=getint();
    for(int i=1;i<n;i++)
    {
        x=getint(),y=getint();
        add(x,y),add(y,x);//建边
    }
    root=getint();
    dfs(root,0);
    cout<<f[root];
    return 0;
}

本题结。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值