1. 程式人生 > 其它 >「UVA 10859」題解

「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.}\)