zoukankan      html  css  js  c++  java
  • [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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    链接数据库
    Ajax 密码验证
    for循环 打印菱形 空 和 实
    for 循环 正方形
    面向对象
    用正则表达式 匹配手机号码
    正则表达式
    js 中 == 和=== 有什么区别?
    js 删除
    封装函数增删改查
  • 原文地址:https://www.cnblogs.com/ninedream/p/13882427.html
Copyright © 2011-2022 走看看