wustoj 1590: As Many Princesses as Possible 树形dp 好题 ★

来源:互联网 发布:basinmod软件下载 编辑:程序博客网 时间:2024/06/06 15:24

1590: As Many Princesses as Possible
Time Limit: 1 Sec  Memory Limit: 128 MB   64bit IO Format: %lld
Submitted: 14  Accepted: 3
[Submit][Status][Web Board]

Description
The big demon king has n houses which make up a tree, each edge's length is one. Now, the demon king catches many princesses that will be put in the houses. However any two princesses can't be put in two houses which the distance between the houses is no more than .
Each house has no more than one princess. The demon king wants to know the maximum number of princesses that he can put in houses.





Input

The first line of input contains the number of test case T(T<=10) . The descriptions of the test cases follow:
The first line contains two integers n(1<=n<=1e5), m(1<=m<=10)
Next n-1 lines: each line contains two numbers ui and vi (1<=ui,vi<=n)  , indicates that there’s one road connecting city ui and vi.

Output

For each test case, output a single integer - denotes the maximum number of princesses imprisoned. 

Sample Input

2
5 1
1 2
1 3
3 4
4 5
5 0
1 2
1 3
3 4
3 5

Sample Output

3
5

Source

武汉科技大学第二届移动互联网应用设计大赛(A类)暨华中地区程序设计竞赛专业组

Author

gogovim


这个是我校一个学长出的题,感觉出的非常好,ac后也很爽。


题意:给出n个点的树,从中选出尽可能多的点,使得其中任意点间的距离>m。输出最大点数 (n<=1e5)


一看就知道是树形dp了,首先最容易想到的是状态表示


dp[x][k]的意思是说:
对于节点x,再向下走k步之后,才能开始选,并且表示最优情况
(所以说该结果优于,至少不输于 dp[x][k+1],dp[x][k+2]);


然后就是状态转移方程:

首先对于一个点x,如果当前状态时dp[x][k]  ,

1.  它的子结点y ,先要满足dp[y][k2],k2>=k-1。

2. 它的子结点相互间的距离不能小于m+1


根据这两点来写 


#include<bits/stdc++.h>using namespace std;#define all(x) (x).begin(), (x).end()#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)typedef long long ll;typedef pair<int, int> pii;const int INF =0x3f3f3f3f;const int maxn= 1e5+0.5   ;int n,m;vector<int >G[maxn+10];int dp[maxn+10][13];int sum[13];int fir[maxn+10], nex[2*maxn+10],u[2*maxn+10],v[2*maxn+10];//边信息int emax;void init()//初始化边集{    emax=0;    memset(fir,-1,(n+1)*sizeof fir[0]);}inline void add_edge(int &x,int &y)//添加边{    int e=emax++;    u[e]=x,v[e]=y;    int t=fir[x];    fir[x]=e;    nex[e]=t;}void dfs(int x,int fa)//dfs遍历,进行树形dp{     for(int e=fir[x];~e;e=nex[e])     {         int y=v[e];if(y==fa) continue;         dfs(y,x);     }//上一部分是得到子树信息/*dp[x][k]的意思是说:对于节点x,再向下走k步之后,才能开始选,并且表示最优情况(所以说该结果优于,至少不输于 dp[x][k+1],dp[x][k+2]);*/    memset(sum,0,(m+1)*sizeof sum[0]);//get sum[k],sum[k]表示dp[y][k]之和,y是x的儿子    for(int k=0;k<=m;k++)    {          for(int e=fir[x];~e;e=nex[e])        {            int y=v[e];if(y==fa) continue;            sum[k]+=dp[y][k];        }    }//dp关键步,先考虑当前结点x不选的情形    for(int k=m;k>=0;k--)    {          for(int e=fir[x];~e;e=nex[e])      {              int y=v[e]; if(y==fa) continue;              int  p=max(0,k-1);              int lea=max(k-1,m+1-2-p);              dp[x][k]=max(dp[x][k],sum[lea]-dp[y][lea]+dp[y][p]);      }      if(k!=m)  dp[x][k]=max(dp[x][k],dp[x][k+1]);//使dp[x][k]包含dp[x][k+1]...dp[x][m]的最优解    }     int ret=1;//考虑当前节点x选了的情形。      for(int e=fir[x];~e;e=nex[e])    {         int y=v[e]; if(y==fa) continue;        ret+=dp[y][m];    }    dp[x][0]=max(dp[x][0],ret);//更新dp[x][0];}int main(){   int T,x,y;scanf("%d",&T);   while(T--)   {       scanf("%d%d",&n,&m);       init();       for(int i=1;i<n;i++)       {           scanf("%d%d",&x,&y);           add_edge(x,y);           add_edge(y,x);       }       if(!m)       {           printf("%d\n",n);continue;       }       memset(dp,0,(n+1)*sizeof dp[0]);       dfs(1,-1);       printf("%d\n",dp[1][0]);   }   return 0;}


0 0
原创粉丝点击