POJ 3659 Cell Phone Network

POJ 3659 Cell Phone Network

题目传送门

题意简述

\(n\) 个节点的树形地图,一个节点如果建立了电话杆,那么和它直接相连的顶点也会被覆盖到,问在这些顶点上最少建多少个电话杆,可以使得所有顶点被覆盖到。

数据范围:\(1\le n\le 10000\)

题目简析

按照套路,我们设计状态 \(dp[i][0/1][0/1]\),表示节点 \(i\) 是否建立电话杆,是否被覆盖,需要的最少电话杆数。

显然,状态 \(dp[i][1][0]\) 是不合法的。我们只需对剩下状态进行转移。

边界条件:\(dp[u][0][0]=0,dp[u][0][1]=inf,dp[u][1][1]=1\)

解释:若自己不放,且被父亲覆盖,则电话杆数需加上儿子的情况,初始为 \(0\);若自己不放,且被儿子覆盖,那么应选最少的一种方案而不是累加,初始为 \(inf\);若自己放,是累加,初始为 \(1\)

  1. \(dp[u][1][1]=\sum\limits_{v\in son[u]}\min\Big(dp[v][0][0],dp[v][0][1],dp[v][1][1]\Big)\)

    这个转移比较显然。如果在点 \(u\) 建立电话杆,那么 \(v\) 可以被 \(u\) 覆盖、被 \(v\) 的儿子覆盖和被自己覆盖。

  2. \(dp[u][0][0]=\sum\limits_{v\in son[u]}\min\Big(dp[v][0][1],dp[v][1][1]\Big)\)

    如果点 \(u\) 不建立电话杆,也没被儿子覆盖,那 \(v\) 必须被覆盖。

    值得注意的是,\(dp[u][0][0]\)\(dp[v][1][1]\) 的转移是错误的,因为如果 \(v\) 建立了电话杆,\(u\) 必然被覆盖。但这一错误对答案并无影响,我们在 \(3\) 个状态的转移讲解结束后再进行解释。

  3. \(dp[u][0][1]=\min\Big(dp[u][0][1]+dp[v][1][1],dp[u][0][1]+dp[v][0][1],dp[u][0][0]+dp[v][1][1]\Big)\)

    • 如果自己已经被之前的儿子覆盖了,但自己又不放,那么当前的儿子必须被覆盖,因此 \(v\) 的状态可以是 \(dp[v][0][1]\)\(dp[v][1][1]\)
    • 如果自己没有被之前的儿子覆盖,且自己不放,那么当前的儿子必须放电话杆,否则不满足状态,无法构成转移。因此 \(v\) 的状态只能是 \(dp[v][1][1]\)

现在再回到第 \(2\) 个状态的问题。如果 \(dp[v][0][0]\) 从它的儿子的 \([1][1]\) 状态转移过来的话,一定会大于 \(dp[v][0][1]\),从而在 \(dp[u][1][1]\) 中不能成为最小值的组成部分被累加上。那做这个“废步”的目的是什么呢?\(dp[v][0][1]\) 的初值为 \(inf\),如果一直没有更新过,那么在第 \(2\) 个状态的转移中会不停地向父亲节点累加,从而导致超过 long long 范围,而造成答案错误。

代码展示

#include<cstdio>
#include<iostream>
using namespace std;

typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int M=1e4+5;

int n;

struct edge
{
	int to,nxt;
}e[M<<1];
int hd[M],cnt;
void add(int x,int y)
{
	e[++cnt]=(edge){y,hd[x]};
	hd[x]=cnt;
}

ll dp[M][2][2];
/*
1/0 be covered by itself  or not
1/0 be covered by its son or not
it's impossible to get to dp[i][1][0]
*/

void dfs(int u,int fa)
{
	dp[u][0][0]=0,dp[u][0][1]=inf,dp[u][1][1]=1;
	ll cost=inf;
	bool fi=false;
	for(int i=hd[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
//		cout<<v<<" ";
		if(v==fa)continue;
		dfs(v,u);
		
		ll tmp=min(dp[u][0][1]+min(dp[v][1][1],dp[v][0][1]),dp[u][0][0]+dp[v][1][1]);
		dp[u][0][1]=tmp;
		dp[u][0][0]+=min(dp[v][0][1],dp[v][1][1]);
		dp[u][1][1]+=min(dp[v][0][0],min(dp[v][0][1],dp[v][1][1]));
	}
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	dfs(1,0);
	ll ans=min(dp[1][0][1],dp[1][1][1]);
	printf("%lld",ans); 
	return 0;
}
posted @ 2021-07-08 21:51  cyl06  阅读(43)  评论(0编辑  收藏  举报