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\)。
-
\(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\) 的儿子覆盖和被自己覆盖。
-
\(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\) 个状态的转移讲解结束后再进行解释。
-
\(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;
}