HDU5090--Game with Pearls 二分图匹配 (匈牙利算法)

题意:给N个容器,每个容器里有一定数目的珍珠,现在Jerry开始在管子上面再放一些珍珠,放上的珍珠数必须是K的倍数,可以不放。最后将容器排序,如果可以做到第i个容器上面有i个珍珠,则Jerry胜出,反之Tom胜出。

思路:数据比较小,所以我是水过的,模拟过程 + 贪心就能过。但正解是二分图匹配,之前没接触过。

二分图匹配:解释一下第二组样例,k = 2;

从左向右,每一个能够匹配 i 的(a),就去掉 i 周围其他的边,然后再也去掉a能指向的边,计算一下共有几个能匹配的,如果是n,则Jerry胜,否则Tom胜;

水过的代码

 1 ///水过的代码
 2 #include <iostream>
 3 #include <cmath>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <string>
 8 #include <sstream>
 9 #include <algorithm>
10 #define Max 2147483647
11 #define INF 0x7fffffff
12 #define N 90010
13 #define ll long long
14 #define mem(a,b) memset(a,b,sizeof(a))
15 #define repu(i, a, b) for(int i = (a); i < (b); i++)
16 const double PI=-acos(-1.0);
17 using namespace std;
18 int a[N];
19 int main()
20 {
21     int n,T,k;
22     cin>>T;
23     while(T--)
24     {
25         cin>>n>>k;
26         repu(i,1,1+n)
27         cin>>a[i];
28         sort(a+1,a+1+n);
29         int ok = -1,i=1;
30         while(i<=n)
31         {
32             if(a[i]>i)
33             {
34                 ok = 1;
35                 break;
36             }
37             else if(a[i]<i)
38             {
39                 a[i]+=k;
40                 sort(a+1,a+1+n);
41                 continue;
42             }
43             else
44                 i++;
45         }
46         if(ok==1)
47             cout<<"Tom
";
48         else
49             cout<<"Jerry
";
50     }
51     return 0;
52 }
View Code

带权值的二分图匹配(改编下列某大神的代码):

 1 ///http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82360#problem/B
 2 #include <iostream>
 3 #include <cmath>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <string>
 8 #include <sstream>
 9 #include <algorithm>
10 #define Max 2147483647
11 #define INF 0x7fffffff
12 #define N 901
13 #define ll long long
14 #define mem(a,b) memset(a,b,sizeof(a))
15 #define repu(i, a, b) for(int i = (a); i < (b); i++)
16 using namespace std;
17 int g[N][N],vis[N],march[N],mat[N];
18 int n;
19 struct S
20 {
21     int val,v;
22     bool operator < (const S& s ) const
23     {
24         return val > s.val;
25     }
26 } p[N];
27 int dfs( int u )
28 {
29     for( int v = 1 ; v <= n ; v++ ) ///考虑所有 Yi 顶点 v
30     {
31         if(g[u][v]&&!vis[v]) ///v 与 u 邻接,且没有访问过
32         {
33             vis[v] = 1;///如果 v 没有匹配,或者 v 已经匹配了,但从 march[v]出发可以找到一条增广路
34             if(march[v] == -1 || dfs(march[v])) ///注意如果前一个条件成立,则不会递归调用
35             {
36                 march[v] = u; ///把 v 匹配给 u
37                 return 1; ///找到可增广路
38             }
39         }
40     }
41     mat[u] = 0;///如果不存在从u出发的增广路
42     return 0 ;
43 }
44 
45 void MaxMatch( ) ///求二部图最大匹配的匈牙利算法
46 {
47     int res = 0; ///所求得的最大匹配
48     for(int i = 1 ; i <= n ; i++)
49     {
50         memset(vis , 0 , sizeof(vis)) ;
51         int u = p[i].v;///按照排好的权值一个个寻找匹配点
52         res += dfs(u);
53     }
54     ///res是能够匹配的个数
55     repu(i,1,n+1)
56     {
57         int t = march[i];///在dfs中是把v匹配给u,因此需要进一步转化
58         mat[t] = i;
59     }
60     for(int i = 1; i < 1+n; i++)
61         if(i==1)
62             printf("%d", mat[i]);
63         else
64             printf(" %d", mat[i]);
65 }
66 int main()
67 {
68     int a,b,m;
69     scanf("%d",&n);
70     memset(g,0,sizeof(g));
71     memset(march,-1,sizeof(march));
72     memset(mat,0,sizeof(mat));
73     repu(i,1,1+n)
74     {
75         scanf("%d",&a);
76         p[i].val = a;
77         p[i].v = i;
78     }
79     sort(p+1,p+1+n);///按照权值排序
80     repu(i,1,1+n)
81     {
82         scanf("%d",&m);
83         repu(j,0,m)
84         {
85             scanf("%d",&b);
86             g[i][b] = 1;
87         }
88     }
89     MaxMatch();
90     return 0;
91 }
View Code

二分图匹配的代码(某个大神的代码,先留着):

View Code

POJ1469是一个裸的二分图匹配,有p个课程,n个学生,求是否能让每个课程都有学生。

 1 ///转自:http://blog.csdn.net/hackbuteer1/article/details/7398008
 2 /*==================================================*
 3 | 二分图匹配(匈牙利算法DFS 实现)
 4 | INIT: g[][]邻接矩阵;
 5 | 优点:实现简洁容易理解,适用于稠密图,DFS找增广路快。
 6 | 找一条增广路的复杂度为O(E),最多找V条增广路,故时间复杂度为O(VE)
 7 ==================================================*/
 8 #include<stdio.h>
 9 #include<memory.h>
10 bool g[110][310]; ///邻接矩阵,true代表有边相连
11 bool flag,visit[310];    ///记录V2中的某个点是否被搜索过
12 int match[310];   ///记录与V2中的点匹配的点的编号
13 int p,n;   ///二分图中左边、右边集合中顶点的数目
14 
15 /// 匈牙利算法
16 bool dfs(int u)
17 {
18     for (int i = 1; i <= n; ++i)
19     {
20         if (g[u][i] && !visit[i])   ///如果节点i与u相邻并且未被查找过
21         {
22             visit[i] = true;   ///标记i为已查找过
23             if (match[i] == -1 || dfs(match[i]))   ///如果i未在前一个匹配M中,或者i在匹配M中,但是从与i相邻的节点出发可以有增广路径
24             {
25                 match[i] = u;  ///记录查找成功记录,更新匹配M(即“取反”)
26                 return true;   ///返回查找成功
27             }
28         }
29     }
30     return false;
31 }
32 
33 int main(void)
34 {
35     int i,j,k,t,v,ans;
36     scanf("%d",&t);
37     while (t--)
38     {
39         scanf("%d %d", &p, &n);
40         for (i = 1; i <= p; i++)
41         {
42             for (j = 1; j <= n; j++)
43                 g[i][j] = false;
44         }
45         for (i = 1; i <= n; i++)
46             match[i] = -1;
47         flag = true;
48         for (i = 1; i <= p; i++)
49         {
50             scanf("%d",&k);
51             if (k == 0)
52                 flag = false;
53             while (k--)
54             {
55                 scanf("%d",&v);
56                 g[i][v]  = true;
57             }
58         }
59         if (flag)
60         {
61             ans = 0;
62             for (i = 1; i <= p; i++)
63             {
64                 memset(visit,false,sizeof(visit));   ///清空上次搜索时的标记
65                 if(dfs(i))    ///从节点i尝试扩展
66                     ans++;
67             }
68             if (ans == p)
69                 puts("YES");
70             else
71                 puts("NO");
72         }
73         else
74             puts("NO");
75     }
76     return 0;
77 }
博客讲得很好,很受益

 

原文地址:https://www.cnblogs.com/ACMERY/p/4467842.html