题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1252
题意:给出一棵树,将其分成若干块,使得每块的权值之和均不大于m。求最少需要分成几块?
思路:f[u][id][c][flag]表示当前计算以u的第id个孩子为根的子树,flag表示u与u的父节点的关系:flag=1表示u是独立的一块,
flag=0表示u与u的父节点是一块,c表示u所在块内当前的权值。设u的第id个孩子为v,<u,v>=w。则转移为:
(1)若c+w<=m说明v可以与u在一个块上,则递归f[v][0][c+w][0]和f[u][id+1][len][flag](len为f[v][0][c+w][0]返回的权值);当然此时也可以选择v不与u在一个块上,则递归f[v][0][0][1]和f[u][id+1][c][flag];
(2)若c+w>m,则只能选择递归f[v][0][0][1]和f[u][id+1][c][flag]。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
struct node
{
int v,w,next;
node(){}
node(int _v,int _w)
{
v=_v;
w=_w;
}
};
struct Node
{
int cnt,len;
Node(){}
Node(int _cnt,int _len)
{
cnt=_cnt;
len=_len;
}
};
const int INF=1000000000;
const int MAX=105;
int C,num=0;
vector<node> G[MAX];
node edges[MAX*2];
int head[MAX],e;
int n,m;
Node f[MAX][MAX][MAX][2];
int visit[MAX][MAX][MAX][2];
void Add(int u,int v,int w)
{
edges[e].v=v;
edges[e].w=w;
edges[e].next=head[u];
head[u]=e++;
}
void DFS1(int u,int pre)
{
int i,v,w;
for(i=head[u];i!=-1;i=edges[i].next)
{
v=edges[i].v;
w=edges[i].w;
if(v==pre) continue;
DFS1(v,u);
G[u].push_back(node(v,w));
}
}
Node DFS(int u,int id,int c,int flag)
{
if(id==G[u].size()) return Node(flag,c);
if(visit[u][id][c][flag]) return f[u][id][c][flag];
visit[u][id][c][flag]=1;
Node t1,t2,ans=Node(INF,INF);
int v,w;
v=G[u][id].v;
w=G[u][id].w;
if(c+w<=m)
{
t1=DFS(v,0,c+w,0);
t2=DFS(u,id+1,t1.len,flag);
ans=Node(t1.cnt+t2.cnt,t2.len);
}
t1=DFS(v,0,0,1);
t2=DFS(u,id+1,c,flag);
t2.cnt+=t1.cnt;
if(t2.cnt<ans.cnt||t2.cnt==ans.cnt&&t2.len<ans.len) ans=t2;
return f[u][id][c][flag]=ans;
}
int main()
{
for(scanf("%d",&C);C--;)
{
scanf("%d%d",&n,&m);
int i,u,v,w;
for(i=1;i<=n;i++) G[i].clear(),head[i]=-1;
e=0;
for(i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
Add(u,v,w);
Add(v,u,w);
}
DFS1(1,-1);
memset(visit,0,sizeof(visit));
Node ans=DFS(1,0,0,1);
printf("Case %d: %d\n",++num,ans.cnt);
}
}