poj3659树状DP

时间:2021-09-03 07:06:53
Cell Phone Network
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 6273   Accepted: 2250

Description

Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires him to set up cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (conveniently numbered 1..N) so they can all communicate.

Exactly N-1 pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ AN; 1 ≤ BN; AB) there is a sequence of adjacent pastures such that A is the first pasture in the sequence and B is the last. Farmer John can only place cell phone towers in the pastures, and each tower has enough range to provide service to the pasture it is on and all pastures adjacent to the pasture with the cell tower.

Help him determine the minimum number of towers he must install to provide cell phone service to each pasture.

Input

* Line 1: A single integer: N
* Lines 2..N: Each line specifies a pair of adjacent pastures with two space-separated integers: A and B

Output

* Line 1: A single integer indicating the minimum number of towers to install

Sample Input

5
1 3
5 2
4 3
3 5

Sample Output2
题目大意:就是给你一个树状结构,每一个节点可以看守住它自己以及与他相邻的节点,问你最少需要多少个节点
思路分析:首先是树的建立,这是一个无向图,可以用链式前向星来进行见图
我也是现学现卖,这篇博客不错http://blog.csdn.net/acdreamers/article/details/16902023
见图之后就是确定状态以及状态转移方程

•①dp[i][0]:选点i,并且以点i为根的子树都被覆盖了。

•②dp[i][1]:不选点i,i被其儿子覆盖
•③dp[i][2]:不选点i,i没有被子节点覆盖(被其父亲覆盖)
•第二步:确定状态转移方程
•dp[i][0]=1+Σmin(dp[u][0],dp[u][1],dp[u][2]) (u是i的儿子)
•dp[i][2]=Σ(dp[u][1])
•对于dp[i][1]的讨论稍微复杂一点——他的所有儿子里面必须有一个取dp[u][1]
• 那么:if(i没有子节点)dp[i][1]=INF
•else dp[i][1]=Σmin(dp[u][0],dp[u][1])+inc
•其中对于inc有:
•if(上面式子中的Σmin(dp[u][0],dp[u][1])中包含某个dp[u][0])inc=0;
•else inc=min(dp[u][0]-dp[u][1])。
代码:
/*
poj3659
树的最小支配集
树状DP
by xjy*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=+;
struct node//链式前向星存图
{
int to;
int next;
};
node edge[*maxn];//因为无向,所以同一条边会存两次
bool vis[maxn];
int head[maxn];
int dp[maxn][];//状态,对于树上的任意一个点,其合法状态有三种,被自身覆盖,被父节点覆盖,被子节点覆盖
int n;
int tot;
const int inf=0xfffff;
void add(int x,int y)
{
edge[tot].to=y;//这条边的指向
edge[tot].next=head[x];//x连的上一条边
head[x]=tot++;//记录该点这次出现的边的位置
}
void dfs(int nod)
{
bool flag=true;//在对dp[nod][1]的处理上要用到
dp[nod][]=,dp[nod][]=dp[nod][]=;//初始化
vis[nod]=true;//标记数组,通过标记,使得搜索只能从上往下搜,变成了一棵有向树
int Min=inf;
int v;
for(int i=head[nod];i!=-;i=edge[i].next)//head[nod]存的是最后一次出现该节点的边的下标
{
v=edge[i].to;//这条边指向的点
if(!vis[v])
{
dfs(v);
dp[nod][]+=min(dp[v][],min(dp[v][],dp[v][]));
dp[nod][]+=min(dp[v][],dp[v][]);
if(dp[v][]<=dp[v][])
{
flag=false;
dp[nod][]+=dp[v][];
}
else
{
dp[nod][]+=dp[v][];
Min=min(Min,dp[v][]-dp[v][]);
}
}
}
if(flag) //所有子节点都没有放置,不合题意,选择最优的进行放置
dp[nod][]+=Min;//对于叶子节点,dp[i][1]=inf:
}
void init()
{
int a,b;
memset(head,-,sizeof(head));
memset(vis,false,sizeof(vis));
tot=;
for(int i=;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
init();
dfs();//因为是无向图,选择任意一个节点作为根节点
printf("%d\n",min(dp[][],dp[][]));//选1作为整个树的根节点,不可能出现其被其父节点覆盖的情况
}
return ;
}