http://poj.org/problem?id=1947
给你一棵n个节点的树 问你保存p个节点的子树最少需要切断多少条边
我用邻接表保存树
ans[i][j] 代表了包含i在内的它所要子节点和兄弟子树在内 保存j个点需要最少切断多少边
代码及其注释:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
#include<stack>
#include<algorithm>
#define LL long long
using namespace std;
const int N=155;
const int M=1000001;
struct node
{
struct tt *next;
}mem[N];
struct tt
{
int j;
struct tt *next;
};
int ans[N][N];
void build(int i,int j)
{
struct tt *t=new tt;
t->j=j;
t->next=mem[i].next;
mem[i].next=t;
}
void Dele(int n)
{
struct tt *t;
for(int i=1;i<=n;++i)
{
while(mem[i].next!=NULL)
{
t=mem[i].next;
mem[i].next=t->next;
delete t;
}
}
}
int dp(struct tt *t,int k)
{
if(t==NULL)//如果为空了
{
if(k==0)//保存0个的话反回0
return 0;
else
return M;//否则反回最大
}
if(ans[t->j][k]!=-1)
return ans[t->j][k];
if(k==0)//保存0 个的话 需要切断所以兄弟节点与负节点的连线
{
ans[t->j][k]=1+dp(t->next,k);
return ans[t->j][k];
}
ans[t->j][k]=1+dp(t->next,k);//此节点子树与父节点切断
for(int i=1;i<=k;++i)
{
//递归找最优
ans[t->j][k]=min(ans[t->j][k],dp(mem[t->j].next,i-1)+dp(t->next,k-i));
}
return ans[t->j][k];
}
int main()
{
int n,k;
while(scanf("%d %d",&n,&k)!=EOF)
{
for(int i=1;i<n;++i)
{
int x,y;
scanf("%d %d",&x,&y);
build(x,y);
}
if(n==1)
{
printf("0\n");
continue;
}
int MIN=M;
memset(ans,-1,sizeof(ans));
MIN=min(MIN,dp(mem[1].next,k-1));
for(int i=2;i<=n;++i)
{
MIN=min(MIN,1+dp(mem[i].next,k-1));//不一定值保存 1
}
printf("%d\n",MIN);
Dele(n);
}
}