源程序名 roads.???( pas, c, cpp)
可执行文件名 roads.exe
输入文件名 roads.in
输出文件名 roads.out
【问题描述】
一场可怕的地震后,人们用 N 个牲口棚(1≤N≤150,编号 1..N) 重建了农夫 John 的牧
场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟
一的。因此,牧场运输系统可以被构建成一棵树。 John 想要知道另一次地震会造成多严重
的破坏。有些道路一旦被毁坏,就会使一棵含有 P(1≤P≤N) 个牲口棚的子树和剩余的牲口
棚分离, John 想知道这些道路的最小数目。
【输入】
第 1 行: 2 个整数, N 和 P
第 2..N 行:每行 2 个整数 I 和 J,表示节点 I 是节点 J 的父节点。
【输出】
单独一行,包含一旦被破坏将分离出恰含 P 个节点的子树的道路的最小数目。
【样例输入】
roads.in
11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
【样例输出】
roads.out
2
【样例解释】
如果道路 1-4 和 1-5 被破坏,含有节点( 1, 2, 3, 6, 7, 8)的子树将被分离出来
分析:
用树型动态规划求解。定义f(n, m)为在n为根的子树中取m个节点的最小代价,则状态转移方程为:
f(n, m)=min{f(n0, m0)+f(n1, m1)+f(n2, m2)+…+f(nk, mk)}
其中,n0, n1, n2, …, nk为n的k个儿子,m0+m1+m2+…+mk=m,并且定义f(ni, 0)=1。
最后的结果为:min{f(root, p), min{f(n, p) | n≠root}}
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<assert.h>
FILE *inp,*outp;
struct point{int x;point *son,*bro;};
int num[200],x,y,i,n,p,head;
int g[200][200];
point *a[200],*p1,*p2,*Npo;
int Min(int t1,int t2){
if (t1<t2) return t1;else
return t2;
}
int Max(int t1,int t2){
if (t1>t2) return t1;else
return t2;
}
void Count(int head){
if (head==0) return;
int x;num[head]=1;
if (a[head]->son!=NULL){
x=a[head]->son->x;Count(x);num[head]+=num[x];
};
if (a[head]->bro!=NULL){
x=a[head]->bro->x;Count(x);num[head]+=num[x];
};
}
int King(int head,int p){
if (head==0) return 0;
if (g[head][p]!=-1) return g[head][p];
g[head][p]=num[head]-p;
int t1,t2,x1=a[head]->son->x,x2=a[head]->bro->x;
point *p1;
for (int i=Max(0,p-num[x2]);i<=Min(p,num[x1]+1);i++){
if (i==0) t1=1;else
t1=King(x1,i-1);
t2=King(x2,p-i);
g[head][p]=Min(g[head][p],t1+t2);
}
return g[head][p];
}
main(){
inp=fopen("roads.in","r");assert(inp);
outp=fopen("roads.out","w");assert(outp);
fscanf(inp,"%d%d",&n,&p);
Npo=new point;Npo->x=0;num[0]=0;
for (i=1;i<=n;i++){
a[i]=new point;
a[i]->x=i;a[i]->son=Npo;a[i]->bro=Npo;
}
memset(num,0,sizeof(num));
for (i=1;i<n;i++){
fscanf(inp,"%d%d",&x,&y);num[y]=1;
if (a[x]->son==Npo) a[x]->son=a[y];
else{
p2=a[x]->son;
while (p2->bro!=Npo)
p2=p2->bro;
p2->bro=a[y];
};
}
head=1;
while (num[head]==1) head++;
Count(head);
memset(g,255,sizeof(g));
x=King(head,p);
for (i=1;i<=n;i++)
if (num[i]-num[a[i]->bro->x]==p){
x=Min(x,1);break;
}
fprintf(outp,"%d\n",x);
fclose(outp);
}