题目传送门:【HDU 1054】
题目大意: Bob喜欢玩电脑游戏,尤其是策略游戏,但是有时候他不能很快地找到答案。现在他正面临着一个困难。他必须守护一个中世纪古城,而这个古城内的道路是一棵树。他想要派最少的守城卫士到每个节点上,使这些卫士能够与每条边都相邻。你能帮他解决吗?
输入多组数据。每组数据第一行有一个整数 N ( 0 < N ≤ 1500 ),代表古城内的节点数量。之后 N 行,每行输入两个数 a,b,代表当前节点,和它的子节点数量;接着输入 b 个数,代表子节点的标号。标号从 0 开始。对于每组数据,输出一个整数,代表需要派遣的守城卫士的最小数目。
题目分析:
其实这道题有多种方法,这里我当做了模板题拿来练手。
观察这个图,显然根据题目性质,我们要求这个图的最小点覆盖;因此,我们可以直接将这个图转化为二分图,然后因为最小点覆盖等于最大匹配,跑一遍 Hungary 算法即可。
下面附上代码:
- #include<cstdio>
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- using namespace std;
- const int MX = 4002;
- struct Edge{
- int to,next;
- };
- Edge edge[MX * 2];
- int n,head[MX * 2],now = 0,ans = 0,match[MX * 2];
- bool vis[MX * 2];
- void adde(int u,int v){
- edge[++now].to = v;
- edge[now].next = head[u];
- head[u] = now;
- }
- bool hungary(int u){ //最小点覆盖 = 最大匹配
- for (int i = head[u];i;i = edge[i].next){
- int v = edge[i].to;
- if (!vis[v]){
- vis[v] = true;
- if (match[v] == 0 || hungary(match[v])){
- match[v] = u;
- return true;
- }
- }
- }
- return false;
- }
- void _init(){
- memset(head,0,sizeof(head));
- memset(edge,0,sizeof(edge));
- memset(match,0,sizeof(match));
- now = 0,ans = 0;
- }
- int main(){
- while (scanf(“%d”,&n) != EOF){
- int a,b,c;
- for (int i = 1;i <= n;i++){
- scanf(”%d”,&a);
- getchar();getchar(); //过滤掉:()等字符
- scanf(”%d”,&b);
- getchar();
- for (int i = 1;i <= b;i++){
- scanf(”%d”,&c);
- adde(a + 1,c + 2000); //建立双向边,一共四次
- adde(c + 2000,a + 1);
- adde(c + 1,a + 2000);
- adde(a + 2000,c + 1);
- }
- }
- for (int i = 1;i <= n;i++){
- memset(vis,0,sizeof(vis));
- if (hungary(i)) ++ans;
- }
- printf(”%d\n”,ans / 2); //因为只有一侧,所以答案除以2
- _init();
- }
- return 0;
- }