您的位置:首页 > 其它

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

2015-07-14 14:24 405 查看
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);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: