[CQOI2017] 小Q的棋盘
作者:互联网
[CQOI2017] 小Q的棋盘
题目链接:洛谷P3698
题意简述
给定一棵树,点数为n,从根节点出发,每一步可以走向与当前点有直接边相连的点,问走m步最多能经过多少个点。边和点均可以重复经过,但不重复计数。
算法概述
\(f[p][j]\) 表示从 \(p\) 出发走向以 \(p\) 为根的子树,一共走 \(k\) 步并且最终回到 \(p\),最多能经过的点数。
\(g[p][j]\) 表示从 \(p\) 出发走向以 \(p\) 为根的子树,一共走 \(k\) 步并且最终不回到 \(p\),最多能经过的点数。
考虑 \(f[p][j]\) 的转移,走的情况应该是从 \(p\) 出发,走向某棵子树,再走回 \(p\),再走向某棵子树,再走回 \(p\),……,最后走回 \(p\)。
枚举 \(p\) 的所有子树,对于当前的子树 \(x\),考虑分配给其多少步 \(k\),由于还要走回 \(p\),故 \(k\) 的范围即是 \([0,j-2]\),状态转移方程即为:
\[f[p][j]=\mathop{max}_{2<=j<=m}\{f[p][j-k-2]+f[x][k]\} \]边界:\(f[p][0]=1\)。
考虑 \(g[p][j]\) 的转移,对于当前的子树 \(x\),走的情况无非以下两种:
- 从 \(p\) 出发走向其他子树最后回到 \(p\),再走向 \(x\) 的子树,最后不回来。
- 从 \(p\) 出发走向 \(x\) 的子树最后回到 \(p\) ,再走向 \(p\) 的其他子树,最后不回来。
对于第一种情况:
\[g[p][j]=\mathop{max}_{1<=j<=m,0<=k<=j-1}\{f[p][j-k-1]+g[x][k]\} \]对于第二种情况:
\[g[p][j]=\mathop{max}_{2<=j<=m,0<=k<=j-2}\{g[p][j-k-2]+f[x][k]\} \]边界:\(g[p][0]=1\)。
最后的答案为 \(g[0][1 \ldots m]\) 中的最大值,因为如果不足 \(m\) 步就达到了最大值的话,剩下的步数可以随便走。
参考代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=110;
struct Edge{
int to,nex;
}edge[N<<1];int idx;
int h[N];
void add_edge(int u,int v){edge[++idx]={v,h[u]};h[u]=idx;}
int f[N][N];
int g[N][N];
int n,m;
void dfs(int p,int fa)
{
f[p][0]=g[p][0]=1;
for(int i=h[p];~i;i=edge[i].nex)
{
int to=edge[i].to;
if(to==fa)continue;
dfs(to,p);
for(int j=m;j>=1;j--)
for(int k=j-1;k>=0;k--)
{
g[p][j]=max(g[p][j],f[p][j-k-1]+g[to][k]);
if(j-k-2>=0)
f[p][j]=max(f[p][j],f[p][j-k-2]+f[to][k]),
g[p][j]=max(g[p][j],g[p][j-k-2]+f[to][k]);
}
}
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
for(int i=1,a,b;i<=n-1;i++)
{
scanf("%d%d",&a,&b);
add_edge(a+1,b+1);
add_edge(b+1,a+1);
}
dfs(1,0);
int ans=0;
for(int i=1;i<=m;i++)ans=max(ans,g[1][i]);
printf("%d\n",ans);
return 0;
}
标签:走向,int,max,include,子树,CQOI2017,棋盘,出发 来源: https://www.cnblogs.com/ninedream/p/13882427.html