[NOIP2017模拟]鸭舌

xiaoxiao2021-02-28  109

题目: 小美喜欢吃鸭舌。 有一个 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; }

本题结。

转载请注明原文地址: https://www.6miu.com/read-34301.html

最新回复(0)