原题链接
题目大意
在一个城市里,有 n ( 1 ≤ n ≤ 100000 ) n(1\le n\le 100000) n(1≤n≤100000)个路口和 n − 1 n-1 n−1条马路,而且,每个路口都是互相可达的。现在,我们要在路口上装上电子眼,以监视路面情况。每个路口的电子眼都可以监视所所有与这个路口相通的道路,求最少要装多少个电子眼。
结题思路
我们可以使用树状dp来AC掉这一题。首先,先使用链式储存结构来对每一个相通的路口进行存储:
void add(int x,int y)
{
a[++tot].x=y;
a[tot].last=head[x];//往上一个位置链接
head[x]=tot;//更新最后一个位置
return;
}
由于每个路口都是互相相通的,我们需要进行两次连接:
for(int i=1;i<n;++i){
cin>>x>>y;
add(x,y);
add(y,x);
}
接下来,就是dp了,我们可以开一个二维数组,分别表示当前位置放或不放电子眼。如果放,那么上一个位置,放或不放都可以,取最小值即可;如果不放,那么上一个位置必须放。所以,状态转移方程就为: 设 c h i l d 是 一 个 与 n 联 通 的 点 f = { f 1 n = f 1 n + m i n ( f 1 c h i l d , f 0 c h i l d ) f 0 n = f 0 n + f 1 c h i l d 设child是一个与n联通的点\\f=\left\{\begin{matrix}f_{1_{n}}=f_{1_{n}}+min(f_{1_{child}},f_{0_{child}})\\f_{0_{n}}=f_{0_n}+f_{1_{child}}\end{matrix}\right. 设child是一个与n联通的点f={ f1n=f1n+min(f1child,f0child)f0n=f0n+f1child
代码实现
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
int x,last;
} a[1000010];
int head[1000010],f[2][1000010],n,x,y,tot;
bool v[1000010];
void add(int x,int y)
{
a[++tot].x=y;
a[tot].last=head[x];
head[x]=tot;
return;
}
void dp(int n)
{
f[1][n]=1;//初始化,如果当前放的话,当前至少有一个电子眼
f[0][n]=0;//初始化
for(int i=head[n];i;i=a[i].last){
if(!v[a[i].x]){
//判重,防止死循环
v[a[i].x]=true;//判重,防止死循环
dp(a[i].x);
f[1][n]+=min(f[1][a[i].x],f[0][a[i].x]);//动态转移方程
f[0][n]+=f[1][a[i].x];//动态转移方程
}
}
return;
}
int main()
{
memset(f,0x7f,sizeof(f));
cin>>n;
for(int i=1;i<n;++i){
cin>>x>>y;
add(x,y);
add(y,x);
}
v[1]=true;
dp(1);
cout<<min(f[0][1],f[1][1]);
return 0;
}