http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1785
 /**//*
题意:一棵n个节点的树,每个中间节点最多能让K对叶子通话。每条边最多允许一个通话
现问最多能使多少对叶子通话
tree dp
in[u]表示u及其子树最多能通话的对数
out[u]表示u及其子树最多能通话的对数,但还有一个叶子待通过u的父边连到其他叶子的(该叶子不计入)

可通过背包算出dp[k],占用u的k条线路(指到儿子的边)的最多对数
然后选用k条更新in[u],out[u]
k偶数,表明有k/2对在通话
k奇数,表明有k/2对在通话,而且有一条待连到父边的

n<=10^5 递归会爆 需要非递归dfs 用vector会超时

*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstring>

using namespace std;

const int MAXN = 100010;

struct State
  {
int u,p;
bool flag;
 State(int u,int p,bool flag):u(u),p(p),flag(flag) {}
};

struct Node
  {
int v;
Node *next;
}nodes[MAXN*2],*G[MAXN],*pE;

int N,K;
int in[MAXN],out[MAXN];
int Son[MAXN];

void add(int u,int v)
  {
pE->v=v;
pE->next=G[u];
G[u]=pE;
Son[u]++;
pE++;
}
void dfs(int u,int p)
  {
stack<State>S;
S.push(State(u,p,0));
while(!S.empty())
 {
State top = S.top();S.pop();
int u=top.u,p=top.p;
if(top.flag)
 {
int size=Son[u];
int bound=min(size,2*K);
int dp[30];
memset(dp,-1,sizeof(dp));
dp[0]=0;
in[u]=0;
out[u]=0;
for(Node *pe=G[u];pe;pe=pe->next)
 {
int v=pe->v;
if(v==p)continue;
for(int j=bound;j>=0;j--)
 {
if(dp[j]!=-1)
dp[j]+=in[v];
if(j&&dp[j-1]!=-1)
dp[j]=max(dp[j],dp[j-1]+out[v]);
}
}
for(int k=0;k<=bound;k++)
 {
if(dp[k]==-1)continue;
if(k&1)
out[u]=max(out[u],dp[k]+k/2);
else
in[u]=max(in[u],dp[k]+k/2);
}
}
else
 {
S.push(State(u,p,1));
for(Node *pe=G[u];pe;pe=pe->next)
 {
int v=pe->v;
if(v==p)continue;
S.push(State(v,u,0));
}
}
}
}

int main()
  {
// freopen("in","r",stdin);
int a,b;
while(~scanf("%d%d",&N,&K))
 {
for(int i=1;i<=N;i++)
 {
G[i]=0;
Son[i]=0;
}
pE=nodes;
for(int i=1;i<N;i++)
 {
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
int root=-1;
for(int i=1;i<=N;i++)
 if(Son[i]!=1) {root=i;break;}
if(root!=-1)
 {
dfs(root,0);
printf("%d\n",in[root]);
}
else printf("%d\n",1);
}
return 0;
}

edward_mj 有更好的做法,不用dp,计算一下 直接用一个rest数组记录 剩下多少个需要用连向父亲这条边来解决。 http://hi.baidu.com/edwardmj/blog/item/356c542d2e3e35351f30893d.html
|
|
常用链接
随笔分类
Links
搜索
最新评论

|
|