能量获取
题目描述
“封印大典启动,请出 Nescafe 魂珠!”随着圣主 applepi 一声令下,圣剑护法 rainbow
和魔杖护法 freda 将 Nescafe 魂珠放置于封印台上。 封印台是一个树形的结构, 魂珠放置的
位置就是根节点(编号为 0)。 还有 n 个其它节点(编号 1~n)上放置着封印石, 编号为 i
的封印石需要从魂珠上获取 Ei 的能量。能量只能沿着树边从魂珠传向封印石,每条边有一
个能够传递的能量上限 Wi,魂珠的能量是无穷大的。 作为封印开始前的准备工作,请你求
出最多能满足多少颗封印石的能量需求?
注意: 能量可以经过一个节点,不满足它的需求而传向下一个节点。 每条边仅能传递一
次能量。
输入格式
第一行一个整数 n,表示除根节点之外其它节点的数量。
接下来 n 行,第 i+1 行有三个整数 Fi、 Ei、 Wi,分别表示 i 号节点的父节点、 i 号节点
上封印石的能量需求、连接节点 i 与 Fi 的边最多能传递多少能量。
输出格式
最多能满足多少颗封印石的能量需求。
样例输入
4
0 3 2
0 100 100
1 1 1
2 75 80
样例输出
2
数据范围与约定
对于 100% 的数据,满足 1<=n<=1000, 0<=Fi<=n, 0<=Ei,Wi<=100。
类型:贪心
贪心,每次选取能量需求最小的节点,扫描它到根节点的路径上的边的容量,看能否满足,如果能满足就把它到根节点的路径上的边的容量都减去它的需求即可。
本题如果把握不好贪心的正确性也可以写树状动规(多叉树,背包转移),但是显然编程复杂度就上升了一个层次。
先来个树形DP+分组背包的,由于存图的原因,时间上过不去(如果用儿子兄弟表示法应该能过),但算法是正确的(最慢一组跑了3.8s)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=1e9; 4 const int maxn=1005; 5 const int maxe=105; 6 int N; 7 int f[maxn][maxe];//f[i][j]以i为根节点,传递能量为j时最多的满足个数(不含i) 8 int fa[maxn]; 9 int E[maxn]; 10 int W[maxn]; 11 int vis[maxn]; 12 int w[maxn],c[maxn]; 13 int a[maxn][8000]; 14 int sontot[maxn]; 15 vector<int> son[maxn]; 16 int ANS; 17 int cnt; 18 int tot; 19 inline int ser(){ 20 int x=-1; 21 for(int i=1;i<=N;i++){ 22 if(sontot[i]!=0){ 23 bool jud=false; 24 for(int j=0;j<son[i].size();j++){ 25 if(vis[son[i][j]]==0){ 26 jud=true; 27 break; 28 } 29 } 30 if(jud==true) continue; 31 else{ 32 x=i; 33 break; 34 } 35 } 36 } 37 return x; 38 } 39 inline void calc(int x){ 40 for(int k=1;k<=cnt;k++){ 41 for(int h=W[x];h>=0;h--){ 42 for(int p=1;p<=a[k][0];p++){ 43 if(h>=w[a[k][p]]){ 44 int tmp=a[k][p]; 45 f[x][h]=max(f[x][h],f[x][h-w[tmp]]+c[tmp]); 46 } 47 } 48 } 49 } 50 memset(w,0,sizeof(w)); 51 memset(c,0,sizeof(c)); 52 memset(a,0,sizeof(a)); 53 cnt=0;//分组数 54 tot=0;//总选择个数 55 } 56 int main(){ 57 freopen("energy.in","r",stdin); 58 freopen("energy.out","w",stdout); 59 scanf("%d",&N); 60 for(int i=1;i<=N;i++){ 61 int fat,x,cc; 62 scanf("%d%d%d",&fat,&x,&cc); 63 fa[i]=fat; 64 son[fat].push_back(i); 65 E[i]=x; 66 W[i]=cc; 67 sontot[fat]++; 68 } 69 fa[0]=-1; 70 vis[0]=1; 71 72 for(int i=1;i<=N;i++){//初始化叶子节点 73 if(sontot[i]==0){ 74 if(E[i]<=W[i]){ 75 for(int k=E[i];k<=100;k++) 76 f[i][k]=1;// 第二维:费用 数值:价值 77 } 78 vis[i]=1; 79 } 80 } 81 82 for(;;){ 83 int x=-1; 84 x=ser(); 85 if(x==-1) break; 86 vis[x]=1; 87 for(int i=0;i<son[x].size();i++){//枚举孩子 88 int y=son[x][i];//找到 x的一个孩子 y 89 sontot[x]--; 90 cnt++;//以x的孩子来分组 91 for(int j=0;j<=100;j++){ 92 if(f[y][j]!=0&&j<=W[y]){ 93 a[cnt][++a[cnt][0]]=++tot; 94 w[tot]=j; 95 c[tot]=f[y][j]; 96 } 97 else if(j>W[y]) break; 98 } 99 } 100 cnt++; 101 a[cnt][++a[cnt][0]]=++tot; 102 w[tot]=E[x]; 103 c[tot]=1; 104 calc(x); 105 } 106 107 for(int i=0;i<son[0].size();i++){ 108 int g=son[0][i]; 109 ANS+=f[g][W[g]]; 110 } 111 cout<<ANS; 112 return 0; 113 }
下面是贪心:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct rec{int req,ver;}a[1010]; 7 int fa[1010],cap[1010],n,i,j,ans; 8 bool operator <(rec a,rec b) 9 { 10 return a.req<b.req; 11 } 12 int main() 13 { 14 freopen("energy.in","r",stdin); 15 freopen("energy.out","w",stdout); 16 cin>>n; 17 for(i=1;i<=n;i++) 18 { 19 scanf("%d%d%d",&fa[i],&a[i].req,&cap[i]); 20 a[i].ver=i; 21 } 22 sort(a+1,a+n+1); 23 for(i=1;i<=n;i++) 24 { 25 for(j=a[i].ver;j;j=fa[j]) 26 if(cap[j]<a[i].req) break; 27 if(j) continue; 28 for(j=a[i].ver;j;j=fa[j]) 29 cap[j]-=a[i].req; 30 ans++; 31 } 32 cout<<ans<<endl; 33 return 0; 34 }