「UVA 10859」题解

Description

Link
给定一棵树,选其中的一些点使每条边都与至少一个所选点相邻。
在满足上述条件的情况下,使得被两个点同时相邻的边最多。
其中 \(1\leq n\leq10^3\)


Solution

经典的换根 DP。
如果只考虑要求 1,设 \(dp[i][0]\) 表示 i 选不选(0/1)时照亮所有 i 的子节点所需要的最少灯数。
则:

  • \(dp[i][0]=\sum_{j=1}^{son[i]}dp[j][1]\)
  • \(dp[i][1]=\sum_{j=1}^{son[i]}\min(dp[j][0],dp[j][1])\)

i 选,那么子节点可选可不选,i能全部照亮。
i 不选,那么每条边都只能子节点来照亮,所以所有子节点必选。
\(f[i][0]\) 表示在满足条件 1 的情况下,i 选不选,在 i 的子树中被两个点照亮的边的数量最大值。
如果 i 不选,依然所有的子节点都必选。

  • \(f[i][0]=\sum_{j=1}^{son[i]}f[j][1]\)

否则,查看 \(dp[j][0]\)\(dp[j][1]\) 的值,判断 \(dp[i][0]\) 由哪一个值得来,那么 \(f[i][0]\) 也由对应的 \(f[j][0/1]\) 得来。
最后统计答案时按满足条件 1 的最优情况判断即可。
具体可见代码。

Code:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=1e3+5;
int t,n,m,head[MAXN<<1],cnt,dp[MAXN][2],pd[MAXN][2];
bool vis[MAXN];
struct ren{
	int next,to;
}a[MAXN<<1];
void add(int x,int y)
{
	a[++cnt].to=y;
	a[cnt].next=head[x];
	head[x]=cnt;
}
void dfs(int now,int fa)
{
	vis[now]=1;
	dp[now][1]=1,dp[now][0]=0;
	for(int i=head[now];i;i=a[i].next)
	{
		int v=a[i].to;
		if(v==fa)
		{
			continue;
		}
		dfs(v,now);
		dp[now][1]+=min(dp[v][1],dp[v][0]);
		dp[now][0]+=dp[v][1];
	}
}
void DP(int now,int fa){
	pd[now][0]=pd[now][1]=0;//pd数组即为上文的f数组
	for(int i=head[now];i;i=a[i].next)
	{
		int v=a[i].to;
		if(v==fa)
		{
			continue;
		}
		DP(v,now);
		pd[now][0]+=pd[v][1];
		if(dp[v][0]<dp[v][1])
		{
			pd[now][1]+=pd[v][0];
			continue;
		}
		if(dp[v][1]<dp[v][0])
		{
			pd[now][1]+=pd[v][1]+1;
			continue;
		}
		pd[now][1]+=max(pd[v][0],pd[v][1]+1);
	}
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		int ans=0,tot=0;
		memset(vis,0,sizeof(vis));
		memset(head,0,sizeof(head));
		cnt=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			x++;
			y++;
			add(x,y);
			add(y,x);
		}
		for(int i=1;i<=n;i++)
		{
			if(!vis[i])
			{
				dfs(i,0);
				ans+=min(dp[i][0],dp[i][1]);
				DP(i,0);
				if(dp[i][0]<dp[i][1])
				{
					tot+=pd[i][0];
					continue;
				}
				if(dp[i][1]<dp[i][0])
				{
					tot+=pd[i][1];
					continue;
				}
				tot+=max(pd[i][1],pd[i][0]);
			}
		}
		printf("%d %d %d\n",ans,tot,m-tot);
	}
	return 0;
}

\(\Bbb{End.}\)
\(\Bbb{Thanks}\) \(\Bbb{For}\) \(\Bbb{Reading.}\)

posted @ 2021-08-17 22:32  StranGePants  阅读(49)  评论(0编辑  收藏  举报