poj 1947
题目:http://poj.org/problem?id=1947
题意:找到一棵节点数为P的子树,使切断的边最少。
思路:树形DP,状态转移方程为: d[ u ][ i ] = min(d[ u ][ i ]+1 , min( d[ v ][ j ]+d[ u ][ i-j ])) , j<i 。d[ u ][ i ]表示以u为根节点的节点数为 i 的所需切断的最少边数。先开始想成 d[ u ][ i ] = min(d[ u ][ i ]+1 , d[ v ][ j ]+d[ u ][ i-j ]) 了,后来一看,d[ u ][ i ] 一直在变,而这里的DP过程为是否切断 u 和 v 之间的边,错了,这里来回,搞脑子搞了很久!最后那里,先开始我也写成 ans = INF,然后一遍找最小值了,叫了一遍WA,然后,好吧,又是默然地看了别人的,应该是先令 ans = d[ root ][ p ] ,在一遍找 d[ i ][ p ] ,因为root不需要切断边,而其他点如果要成为ans,那么必须要切断它与它父亲的那条边。做题怎么总感觉差一点,差一点!
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int INF = 0x0fffffff ;
const int MAXN = 155 ;
vector <int> G[MAXN];
int n,m;
int vis[MAXN];
int d[MAXN][MAXN] ;
void dfs(int u)
{
vis[u]=1;
for(int i=0;i<=m;i++)
d[u][i] = INF;
d[u][1]=0;
for(int i=0;i<G[u].size();i++)
{
int v = G[u][i];
if(vis[v]) continue;
dfs(v);
//printf("u = %d,v = %d\n",u,v);
for(int j = m;j>=1;j--)
{
int tmp = d[u][j]+1;
for(int k = 1;k<j;k++)
{
tmp = min(d[v][k] +d[u][j-k],tmp);
}
d[u][j] = tmp;
//printf("d[%d][%d] = %d\n",u,j,d[u][j]);
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
G[i].clear();
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
memset(vis,0,sizeof(vis));
dfs(1);
int ans = d[1][m];
for(int i=2;i<=n;i++)
ans=min(ans,d[i][m]+1);
printf("%d\n",ans);
}
return 0;
}