题干:

Bob enjoys playing computer games, especially strategic games, but sometimes he cannot find the solution fast enough and then he is very sad. Now he has the following problem. He must defend a medieval city, the roads of which form a tree. He has to put the minimum number of soldiers on the nodes so that they can observe all the edges. Can you help him? 

Your program should find the minimum number of soldiers that Bob has to put for a given tree. 

For example for the tree: 

the solution is one soldier ( at the node 1).

Input

The input contains several data sets in text format. Each data set represents a tree with the following description: 


  • the number of nodes
  • the description of each node in the following format
    node_identifier:(number_of_roads) node_identifier1 node_identifier2 ... node_identifiernumber_of_roads
    or
    node_identifier:(0)

The node identifiers are integer numbers between 0 and n-1, for n nodes (0 < n <= 1500);the number_of_roads in each line of input will no more than 10. Every edge appears only once in the input data.

Output

The output should be printed on the standard output. For each given input data set, print one integer number in a single line that gives the result (the minimum number of soldiers). An example is given in the following:

Sample Input

4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)

Sample Output

1
2

题目大意:

给出一个n个结点的树,要求选出其中的一些顶点,使得对于树中的每条边(u, v),u和v至少有一个被选中. 请给出选中顶点数最少的方案. 

解题报告:

    树形图的最小点覆盖、、、直接跑匈牙利也可以过,大概是数据水了。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const int MAX = 2e5 + 5;
int n;
vector<int> vv[1505];
int dp[1505][2];
void dfs(int cur,int root) {
int up = vv[cur].size();
// if(up == 1) {
dp[cur][1]=1;
// }
for(int i = 0; i<up; i++) {
int v = vv[cur][i];
if(v == root) continue;
dfs(v,cur);
dp[cur][0] += dp[v][1];
dp[cur][1] += min(dp[v][0],dp[v][1]);
}
}
int main()
{
int x,num,y;
while(~scanf("%d",&n)) {
for(int i = 0; i<=n; i++) vv[i].clear();
memset(dp,0,sizeof dp);
for(int i = 1; i<=n; i++) {
scanf("%d:(%d)",&x,&num);
if(num == 0) continue;
while(num--) {
scanf("%d",&y);
vv[x].pb(y);vv[y].pb(x);
}
}
//dfs(vv[0][0],0);
dfs(0,0);
printf("%d\n",min(dp[0][0],dp[0][1]));
}
return 0 ;
}

总结:

   几个要注意的地方:

       首先别忘了是从0号点开始编号!!所以vv的初始化要从i=0开始!

      第二那个if num==0  可以不加、

      第三这题双向图单向图都可以做,一般习惯双向图然后dfs中加一个参数root就行了。。。而且main函数调用的时候不需要管root传什么值(实在不行给个-1,,反正用不到这个),,直接(0,0)或者(1,1)都行。。。(当然最好还是传(0,0)。因为这题是恰好了没有单个顶点的情况,万一有这种特例,那就WA了啊)

 

AC代码2:(分析一下)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define PI acos(-1.0)

using namespace std;
typedef long long ll;
const int maxn = 1500+7, INF = 0x3f3f3f3f;
int n, cur, ans;
int f[maxn];
bool vis[maxn];
int next[maxn];
struct edge {
int v, bef;
} e[maxn*2];
void add(int x, int y) {
e[cur].v = y;
e[cur].bef = next[x];
next[x] = cur++;
}
void init() {
memset(next, -1, sizeof(int)*(n+1));
cur = 1;
for(int j = 0; j < n; ++j) {
int u, v, cnt;
scanf("%d:(%d)", &u, &cnt);
for(int i = 0; i < cnt; ++i) {
scanf("%d", &v);
add(u, v);
add(v, u);
//a[u].push_back(v);
//a[v].push_back(u);
}
}
memset(f, -1, sizeof(int)*(n+1));
}
bool dfs(int id) {
for(int i = next[id]; i != -1; i = e[i].bef) {
if(!vis[e[i].v]) {
vis[e[i].v] = 1;
if(f[e[i].v] == -1 || dfs(f[e[i].v])) {
f[id] = e[i].v;
f[e[i].v] = id;
return true;
}
}
}
return false;
}
int main() {
while(scanf("%d", &n) != EOF && n) {
init();
ans = 0;
for(int i = 0; i < n; ++i) {
if(f[i] == -1) {
memset(vis, false, sizeof(bool)*(n+1));
if(dfs(i)) ans++;
}
}
printf("%d\n", ans);
}
return 0;
}

我们来分析一下这份冗长的代码(网上找的,上面这份是原始代码)首先他用f数组(也就是nxt)数组同时记录了两个的值(与这个题不同​​【HDU - 1281 】棋盘游戏​​,那个题的AC代码2 是 用两个数组分别记录左侧的值和右侧的值的,,而这个题的这种解法是都存在同一个数组f中)所以我们要想直接输出ans而不是ans/2的话,就需要判断一步  :(   if(f[i] != -1)则进入循环 )。如果去掉这个if的话,最后还是要输出ans/2的,,因为相当于还是每个点都搜了一遍,,原来的匹配会被覆盖的,,所以加这个if的话,会减少一半的复杂度(其实也没多少、。、还是那个数量级)(其实我也不太懂,,,不加这个if,,为什么不会wa??我感觉会WA的啊)其实这题跑匈牙利的话直接建双向边上模板就好了。。。就是个拆点

 

下面是个完整的匈牙利代码:(改天可以自己再写一遍)

其实之所以可以跑二分匹配,是因为它其实是个二分图。(所以可以“拆点”(伪拆点)求最大匹配!!!)想想为啥是个二分图。

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#define maxn 210
const int INF=0x3f3f3f3f;
using namespace std;
int n;
vector<int>g[1510];
int used[1510],match[1510];
int dfs(int x)
{
int i;
for(i=0;i<g[x].size();i++)
{
int k=g[x][i];
if(!used[k])
{
used[k]=1;
if(match[k]==-1||dfs(match[k]))
{
match[k]=x;
return 1;
}
}
}
return 0;
}
int main ()
{
int i,j,a,b,k;
while(scanf("%d",&n)!=EOF)
{
int ans=0;
memset(g,0,sizeof(g));
for(i=1;i<=n;i++)
{
scanf("%d:(%d)",&a,&k);
while(k--)
{
scanf("%d",&b);
g[a].push_back(b);
g[b].push_back(a);
}
}
memset(match,-1,sizeof(match));
for(i=0;i<n;i++)
{
memset(used,0,sizeof(used));
if(dfs(i))
ans++;
}
printf("%d\n",ans/2);
}
return 0;
}

附:

不知道这个代码为什么找第一个flag=0的点进行dfs。、。。感觉随便找个点dfs就可以啊,所以0号点就好了啊。

 

一个看不懂的代码:大概是找增广路的方法。匹配变成不匹配。

【POJ - 1463】Strategic game (树上最小点覆盖,树形dp)_#include