UVA11419 SAM I AM 二分图最小点覆盖

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2414

Solution

看到求最小大炮轰的次数我们很容易想到二分图的最小点覆盖,就是求最少哪些行或者列开出炮弹可以使得全部怪物被打死掉,那么我们行列建图,求一个最大匹配就行了

数据范围匈牙利即可,跑完一个最大匹配后,得到了数字答案,那么问题来了,如何得到在哪些行或者列打出炮弹呢。

我们结合图上来看

对于这个图,我们跑完匈牙利匹配后,1和2有连接,3和3有连接,我们输出应该是c2,r3/c3

可以这样想,左边已经匹配完成的点不管它,我们把左边没有匹配的点重新匹配,这时候重新被访问的左端节点就是不能开出炮弹的行(有更优的列值)

怎么说呢,我们看图模拟一边,首先左点2没有连接,从它开始匹配,找到右2,右2已经有匹配左点1,试图给左1分配一个新的点,然而没有,增广失败,

这次寻找增广路我们从左2找到了右2再到左1终止,那这时就把左点1,2加入不可开炮队列,而将右点2加入可开炮队列(因为右点2可以一枪打死俩,更加优秀)

而重新访问到的右点则是需要开出炮弹的列,这种情况在上图没有出现,和上面讨论类似。

  1 /*
  2     最小点覆盖+找出最小点覆盖的点集
  3  */
  4 #include <bits/stdc++.h>
  5 #define lson rt << 1, l, mid
  6 #define rson rt << 1 | 1, mid + 1, r
  7 using namespace std;
  8 using ll = long long;
  9 using ull = unsigned long long;
 10 using pa = pair<int, int>;
 11 using ld = long double;
 12 int n, m, k;
 13 const int maxn = 1e3 + 10;
 14 template <class T>
 15 inline T read(T &ret)
 16 {
 17     int f = 1;
 18     ret = 0;
 19     char ch = getchar();
 20     while (!isdigit(ch))
 21     {
 22         if (ch == '-')
 23             f = -1;
 24         ch = getchar();
 25     }
 26     while (isdigit(ch))
 27     {
 28         ret = (ret << 1) + (ret << 3) + ch - '0';
 29         ch = getchar();
 30     }
 31     ret *= f;
 32     return ret;
 33 }
 34 template <class T>
 35 inline void write(T n)
 36 {
 37     if (n < 0)
 38     {
 39         putchar('-');
 40         n = -n;
 41     }
 42     if (n >= 10)
 43     {
 44         write(n / 10);
 45     }
 46     putchar(n % 10 + '0');
 47 }
 48 int llink[maxn], rlink[maxn], visl[maxn], visr[maxn];
 49 vector<int> g[maxn];
 50 inline void init()
 51 {
 52     memset(llink, -1, sizeof(int) * (m + 2));
 53     memset(rlink, -1, sizeof(int) * (n + 2));
 54     for (int i = 1; i <= n; i++)
 55         g[i].clear();
 56 }
 57 inline void add(int x, int y)
 58 {
 59     g[x].emplace_back(y);
 60 }
 61 int find(int u)
 62 {
 63     visl[u] = 1;
 64     int sz = g[u].size();
 65     for (int i = 0; i < sz; i++)
 66     {
 67         int v = g[u][i];
 68         if (!visr[v])
 69         {
 70             visr[v] = 1;
 71             if (llink[v] == -1 || find(llink[v]))
 72             {
 73                 llink[v] = u;
 74                 rlink[u] = v;
 75                 return 1;
 76             }
 77         }
 78     }
 79     return 0;
 80 }
 81 int maxMacth()
 82 {
 83     int res = 0;
 84     for (int i = 1; i <= n; i++)
 85     {
 86         memset(visr, 0, sizeof(int) * (m + 1));
 87         res += find(i);
 88     }
 89     return res;
 90 }
 91 int main(int argc, char const *argv[])
 92 {
 93     ios::sync_with_stdio(false);
 94     cin.tie(0);
 95     cout.tie(0);
 96 #ifndef ONLINE_JUDGE
 97     freopen("in.txt", "r", stdin);
 98     freopen("out.txt", "w", stdout);
 99 #endif
100     while (cin >> n >> m >> k && (m + n + k))
101     {
102         init();
103         for (int i = 0; i < k; i++)
104         {
105             int x, y;
106             cin >> x >> y;
107             add(x, y);
108         }
109         int ans = maxMacth();
110         memset(visl, 0, sizeof visl);
111         memset(visr, 0, sizeof visr);
112         for (int i = 1; i <= n; i++) //再次匹配,找到哪些点需要匹配
113             if (rlink[i] == -1)
114                 find(i);
115         cout << ans << " ";
116         for (int i = 1; i <= n; i++)
117             if (!visl[i])
118                 cout << "r" << i << " ";
119         for (int i = 1; i <= m; i++)
120             if (visr[i])
121                 cout << "c" << i << ' ';
122         cout << "\n";
123     }
124     return 0;
125 }
View Code

 

转载于:https://www.cnblogs.com/mooleetzi/p/11317779.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值