一、树形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);
}
源程序名 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);
}
相关文章推荐
- 集中化运维管理——Puppet管理之路
- 通过sql做数据透视表,数据库表行列转换(pivot和Unpivot用法)(一)
- 触发器应用 trigger
- html5 webApp常用Meta标签
- MySql时间戳的问题。
- apache-nutch-1.10 安装笔记
- Java WebService 简单实例
- 通过反编译字节码来理解 Java 枚举
- Kettle变量和自定义java代码的实例应用
- mysql的备份脚本
- 4.c语言的进制转换
- mumu血压计母亲节“拼团”盛大开幕,百度和厂家创造一个双赢的局面
- Fresco 获得Bitmap
- UI1_UITableViewHomeWork
- 程序员资源导航
- 另一种拼接数据方法,利用数组的传引用做的。
- 正则表达式--常用
- CT-LIBRARY error:ct_connect(): protocol specific layer: internal Client Library error: There is a td
- 文章标题
- FFmpeg转HTML5支持的视频格式