zoukankan      html  css  js  c++  java
  • POJ 1904 King's Quest (强连通分量+完美匹配)

    <题目链接>

    题目大意:

      有n个王子,每个王子都有k个喜欢的妹子,每个王子只能和喜欢的妹子结婚,大臣给出一个匹配表,每个王子都和一个妹子结婚,但是国王不满意,他要求大臣给他另一个表,每个王子可以和几个妹子结婚,按序号升序输出妹子的编号,这个表应满足所有的王子最终都有妹子和他结婚。

    解题分析:  <转载于 >>> >

      如果王子u喜欢妹子v,则建一条边u指向v(u,v),对于大臣给出的初始完美匹配,如果王子u和妹子v结婚,则建一条边v指向u(v,u),然后求强连通分量。对于每个王子和妹子,如果他们都在同一个强连通分量内,则他们可以结婚。

      为什么呢?因为每个王子只能和喜欢的妹子结婚,初始完美匹配中的丈夫和妻子之间有两条方向不同的边可以互达,则同一个强连通分量中的王子数和妹子数一定是相等的,若王子 x 可以和另外的一个妹子 a 结婚,妹子 a 的原配王子 y 肯定能找到另外一个妹子 b 结婚,因为如果找不到的话,则 x 和 a 必不在同一个强连通分量中。

    所以一个王子可以和所有与他同一强连通分量的妹子结婚,而这不会导致同一强连通分量中的其他王子找不到妹子结婚。

    (证明:王子为什么不能选择不同强连通分量的妹子:

      反证法:如果强连通分量 1 中的王子选择了强连通分量 2 中的妹子,那么势必强连通分量 2 中的一个王子无法在自己的强连通分量中找到妹子,那么他就会去别的强连通分量找妹子,这样一直循环下去,我们知道最终一定是经过了强连通分量 1,2,x1,x2,xn,……,1,王子们才能都找到自己的妹子,这样这些强连通分量1,2,x1,x2,xn,……,1会构成一个强连通分量,与题设在不同强连通分量中找妹子不符)。

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    using namespace std;
    
    #define clr(a, b) memset(a, b, sizeof(a))
    const int N = 5e3 + 10;
    
    int n, tot, scc, top;
    int stk[N], dfn[N], low[N], belong[N], instack[N], ans[N];
    vector<vector<int> > G;
    void init() {
      tot = scc = top = 0;
      clr(dfn, 0);clr(stk, 0);clr(low, 0);clr(belong, 0);clr(instack, 0);
      G.clear(), G.resize(N);
    }
    void Tarjan(int u) {
      low[u] = dfn[u] = ++tot;
      stk[++top] = u;
      instack[u] = 1;
      int v;
      for (int i = 0; i < G[u].size(); i++) {
        v = G[u][i];
        if (!dfn[v]) {
          Tarjan(v);
          low[u] = min(low[u], low[v]);
        } else if (instack[v]) low[u] = min(low[u], dfn[v]);
      }
      if (low[u] == dfn[u]) {
        ++scc;
        do {
          v = stk[top--];
          instack[v] = 0;
          belong[v] = scc;  //将该强连通块缩点染色
        } while (v != u);
      }
    }
    int main() {
      while (scanf("%d", &n) != EOF) {
        init();
        int k, x;
        for (int i = 1; i <= n; i++) {
          scanf("%d", &k);
          while (k--) {
            scanf("%d", &x);
            G[i].push_back(x + n);  //王子编号为1~n,公主编号为n+1~2*n
          }
        }
        for (int i = 1; i <= n; i++) {
          scanf("%d", &x);
          G[x + n].push_back(i);  //根据该完美匹配,让公主与对应的王子连边
        }
        for (int i = 1; i <= n; i++) if (!dfn[i]) Tarjan(i);
        for (int i = 1; i <= n; i++) {
          int u = belong[i], v;
          clr(ans, 0);  // ans[]存所有能够与当前王子进行配对的公主
          int cur = 0;
          for (int j = 0; j < G[i].size(); j++) {  //找出当前王子所在联通块的公主数量
            v = belong[G[i][j]];
            if (u == v) ans[cur++] = G[i][j];
          }
          sort(ans, ans + cur);           
          printf("%d ", cur);
          for (int i = 0; i < cur; i++) {
            printf("%d%s", ans[i] - n, i == cur - 1 ? "
    " : " ");
          }
        }
      }
    }
  • 相关阅读:
    KindEditor限制输入字数
    datepicker日历控件使用
    复合注解的解析
    jdk包结构及用途分析
    深入理解BIO、NIO、AIO
    MiniCat:手写Http服务器
    基于TCP协议的Socket编程
    手写一个最迷你的Web服务器
    手写一个Web服务器,极简版Tomcat
    java网络编程socket的使用
  • 原文地址:https://www.cnblogs.com/00isok/p/9934779.html
Copyright © 2011-2022 走看看