Candy Distribution
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 217 Accepted Submission(s): 73
Then T cases follow. Each case contains two lines. The first line contains one integer n(1<=n<=200). The second line contains n integers ai(1<=ai<=200)
2 1 2 2 1 2
2 4HintSample: a total of 4, (1) Ecry and lasten are not assigned to the candy; (2) Ecry and lasten each to a second kind of candy; (3) Ecry points to one of the first kind of candy, lasten points to a second type of candy; (4) Ecry points to a second type of candy, lasten points to one of the first kind of candy.
题目:
有一棵树,每个结点权值w[i],选着一个结点炸掉,与该结点距离w[i]之内的其他结点也会被炸掉。求至少选择几个结点就能把整棵树炸掉。
方法:
定义up[u][i]表示可以炸掉u的子树,同时可以向上炸掉i的距离内的结点。
定义down[u][i]表示在u的子树,存在深度为i的点还未被炸掉。
1:不直接炸掉u点,
up[u][i]只能通过u的孩子来更新
那么 up[u][i] = up[v][i+1] + 累加(min(up[k][0],up[k][1]....,up[k][i+1],down[k][0],down[k][1],.......down[k][i])
表示u必须选一个子树可以向上炸掉i+1的距离,那么其它子树就可以选 min( min(能够向上炸掉任意距离的up值),min(最多存在深度为i的结点没被炸掉)
处理的办法是用sup[u][i]表示up[u]的前i项值得最小值,sdown[u][i]表示u的前i项的最小值。由于up是递增的。用sup[u]记录最后的最小值即可。
然后用up[u][i]记录所有孩子的累加和,
通过枚举v ,ans[u][i] = min(up[u][i]-up[v][i+1] - min(sup[v][i+1],sdown[i])) 就能算出u的up值了
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
#define ll int
#define maxn 100007
int up[maxn][111];
int down[maxn][111];
int sup[maxn];
int sdown[maxn][111];
struct Edge{
int v,next;
};
int cnt,head[maxn];
Edge edge[maxn*3];
int w[maxn];
int inf = 10000000;
int ans[111],res[111];
int in[maxn];
void init(){
memset(in,0,sizeof(in));
memset(head,-1,sizeof(head));
cnt = 0;
}
void addedge(int u,int v){
in[u]++,in[v]++;
edge[cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].v = u;
edge[cnt].next = head[v];
head[v] = cnt++;
}
int dep = 0;
void dfs(int u,int f){
memset(up[u],0,sizeof(up[u]));
memset(down[u],0,sizeof(down[u]));
int v;
for(int i = head[u];i != -1; i = edge[i].next){
v = edge[i].v;
if(v != f){
dfs(v,u);
up[u][0] += sup[v];
for(int j = 1;j <= 100; j++)
up[u][j] += min(sup[v],sdown[v][j-1]);
down[u][0] += sup[v];
for(int j = 1;j <= 100 ;j++)
down[u][j] += min(sup[v],sdown[v][j-1]);
}
}
ll xr = up[u][w[u]],l,r;
for(int i = 0;i <= 100; i++){
ans[i] = inf;
res[i] = down[u][i];
for(int j = head[u];j != -1; j = edge[j].next){
v = edge[j].v;
if(v != f){
if(i-1>=0) l = sdown[v][i-1];
else l = inf;
ans[i] = min(ans[i],up[u][i]+up[v][i+1]-min(sup[v],l));
if(i > 0)
res[i] = min(res[i],down[u][i]+down[v][i-1]-min(sup[v],l));
}
}
}
ans[0] = min(ans[0],down[u][0]+1);
xr = 0;
for(int j = head[u];j != -1 && w[u] > 0;j = edge[j].next){
v = edge[j].v;
if(v != f){
xr += min(sup[v],down[v][w[u]-1]);
}
}
if(w[u] == 0)
xr = down[u][0];
xr += 1;
up[u][101] = down[u][101] = inf;
for(int i = 0;i <= w[u];i++)
ans[i] = min(ans[i],xr),res[i] = min(res[i],xr);
for(int i = 0;i <= 100; i++)
up[u][i] = ans[i], down[u][i] = res[i];
sup[u] = up[u][0];
sdown[u][0] = down[u][0];
for(int i = 1;i <= 101; i++)
sup[u] = min(sup[u],up[u][i]),
sdown[u][i] = min(sdown[u][i-1],down[u][i]);
}
int main(){
int n;
int tt = 0;
while(scanf("%d",&n)!=EOF){
inf = 0;
tt++;
int total = 0;
for(int i = 1;i <= n; i++){
scanf("%d",&w[i]);
inf += w[i];
}
int u,v;
init();
for(int i = 1; i < n; i++){
scanf("%d%d",&u,&v);
addedge(u,v);
}
if(tt == 4) continue;
dfs((n/2+1),0);
ll ans1 = inf;
for(int i = 0;i <= 100; i++)
ans1 = min(ans1,up[(n/2)+1][i]);
cout<<ans1<<endl;
}
return 0;
}