一、树形dp(3)重建道路

3、 重建道路
源程序名 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);
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值