2021寒假图论复习计划(2)(hduoj3639,hduoj1045,hduoj2444,hduoj1083)

Hawk-and-Chicken(2010 ACM-ICPC Multi-University Training Contest(19)——Host by HDU)(Tarjin)

judge: HDUOJ3639

题意

n n n 个学生,每个学生都有一张选票,一共进行 m m m 次投票。每次会有一个学生投票给另一个学生。

票可以累加传递,但学生不能从自己手里获得选票。

样例1. 当 A A A-> C C C, B B B-> C C C, C C C-> D D D, C C C-> E E E时, A A A, B B B的票数为 0 0 0 C C C的票数为 2 2 2 D D D, E E E的票数为 3 3 3

S A A C C A->C D D C->D E E C->E B B B->C

样例2. 当 A A A-> B B B, B B B-> C C C, C C C-> A A A时, A A A, B B B, C C C都获得 2 2 2票。

S A A B B A->B C C B->C C->A

获得最多票的学生获胜,当有多个学生都持有最多票数的票时,他们同时为获胜者。求出获胜者的票数以及获胜者的名单。

题解

由于样例2,可以知道每个连通块内的学生的选票相同。所以我们需要缩点。

需要注意的是缩完点之后,关系依然是个图而不是树。

比较棘手的情况就是样例1里的情况,投票链的开始和结尾都有多个端点。所以就需要枚举每一个端点,顺序或者逆序遍历整个图。我选择了逆序遍历。也就是枚举每一个只有入度的点作为起点,反向搜索给自己投票的人。

为了避免重复统计,枚举不同起点时需要给沿途加上不同的记号,以标识哪些点是这次走过和未走过的。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 100005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct poi {

};

int n, m;
int num[maxn];
int vis[maxn];
int numOfScc[maxn]; //每个连通块的节点个数
int ind[maxn];
vector<int> G[maxn], g[maxn];

int scccnt;         //强连通分量的数量
int sccno[maxn];    //每个点所在的强连通分量的编号,编号从1开始递增
int dfn[maxn];      //深度优先搜索遍历时结点 u 被搜索的次序
int low[maxn];      //u或u的子树能够追溯到的最早的栈中节点的次序号
int tclock;         //用于递增dfn
stack<int> q;
void init() {
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(sccno, 0, sizeof(sccno));
    memset(num, 0, sizeof(num));
    memset(vis, 0, sizeof(vis));
    memset(numOfScc, 0, sizeof(numOfScc));
    memset(ind, 0, sizeof(ind));
    scccnt = tclock = 0;
    _for(i, maxn) G[i].clear(), g[i].clear();
    while(q.size()) q.pop();
}
void tarjin(int u) {
    dfn[u] = low[u] = ++tclock;
    q.push(u);
    for (int v : G[u]) {
        if (!dfn[v]) {
            tarjin(v);
            low[u] = min(low[u], low[v]);
        }
        else if (!sccno[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (dfn[u] == low[u]) {
        scccnt++;
        int v = -1;
        while (v != u) {
            v = q.top();
            q.pop();
            sccno[v] = scccnt;
        }
    }
}

int dfs(int u, int fl) {
    if(vis[u] == fl) return num[u] + 1;
    vis[u] = fl;
    num[u] = numOfScc[u] - 1;
    for(int v : g[u]) {
        if(vis[v] != fl) num[u] += dfs(v, fl);
    }
    return num[u] + 1;
}

void sol(int ttt) {
    init();
    _for(i, m) {
        int u = read(), v = read();
        G[u].push_back(v);
    }
    _for(i, n) {
        if(!dfn[i]) tarjin(i);
    }
    _for(i, n) ++numOfScc[sccno[i]];
    _for(u, n) {
        for(int v : G[u]) {
            if(sccno[u] != sccno[v]) {
                g[sccno[v]].push_back(sccno[u]);
                ++ind[sccno[u]];
            }
        }
    }
    _rep(u, 1, scccnt) {
        sort(g[sccno[u]].begin(), g[sccno[u]].end());
        g[sccno[u]].erase(unique(g[sccno[u]].begin(), g[sccno[u]].end()), g[sccno[u]].end());
    }
    int ma = -1;
    _rep(i, 1, scccnt) if(ind[i] == 0) {
        num[i] = dfs(i, i) - 1;
        ma = max(ma, num[i]);
    }
    vector<int> ans;
    printf("Case %d: %d\n", ttt, ma);
    _for(i, n) if(num[sccno[i]] == ma) ans.push_back(i);
    _for(i, ans.size()) printf("%d%s", ans[i], nl(i, ans.size()));
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    // debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    int T = read();
    _for(i, T) {
        n = read(), m = read();
        sol(i + 1);
    }

    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}

Fire Net(二分图匹配 or 网络流)

OJ: HDUOJ 1045
VJudge

题意

有一个 n × n n\times n n×n 的网格地图,其中有一些位置是墙,其余地方是空地。

地堡可以攻击上下左右四个方向上的其他地堡,不过墙可以隔绝地堡的攻击。

求出这个地图上最多可以放几个地堡,且它们不互相攻击。

题解

以这张图为例:

将地图以横向划分成若干个区域,每个区域是一个冲突域,冲突域内最多只能放一个地堡。

同时,纵向也可以划分成若干个区域。

这样我们就有了实体,当我们在 ( 2 , 1 ) (2,1) (2,1) 位置放置地堡时,就相当于选择了 3 3 3 号和 6 6 6 号区域,本着同一个冲突域内最多只有一个地堡的原则, 3 3 3 号和 6 6 6 号区域覆盖的网格内都不能再放置别的地堡。

就相当于把横向的区域划分作为左边点集,纵向的区域划分作为右边点集,相交的区域之间连边。在 ( 2 , 1 ) (2,1) (2,1) 位置放地堡就相当于选了 3 3 3 号和 6 6 6 号之间的边加入匹配。

然后就可以进行二分图最大匹配或者跑最大流了。

S S S 1 1 S->1 2 2 S->2 3 3 S->3 4 4 S->4 5 5 S->5 T T 6 6 1->6 10 10 2->10 11 11 2->11 3->6 8 8 3->8 3->10 3->11 4->10 4->11 7 7 5->7 9 9 5->9 5->11 6->T 7->T 8->T 9->T 10->T 11->T

代码

网络流-最大流
// #pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 105;
const int maxm = 25;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
char s[maxn][maxn];
int num[maxn][maxn];

struct edge {
    int to, cap, rev; //用于表示边的结构体(终点,容量,反向边)
    edge() {}
    edge(int t, int cap, int rev) :to(t), cap(cap), rev(rev) {}
};

vector <edge> mf_G[maxm]; //图的邻接表表示
int mf_dis[maxm];   //顶点到源点的距离标号
int mf_cur[maxm];   //当前弧,在其之前的边已经没有用了

void mf_init(){
    for(int i = 0; i < maxn; i++) mf_G[i].clear();
}

void mf_addedge(int from, int to, int cap) {
    mf_G[from].push_back(edge(to, cap, mf_G[to].size()));
    mf_G[to].push_back(edge(from, 0, mf_G[from].size() - 1));
}

void mf_bfs(int s) {//通过mf_bfs计算从源点出发的距离标号
    memset(mf_dis, -1, sizeof(mf_dis));
    queue <int> q;
    mf_dis[s] = 0;
    q.push(s);
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (int i = 0; i < mf_G[v].size(); i++) {
            edge &e = mf_G[v][i];
            if (e.cap > 0 && mf_dis[e.to] < 0) {
                mf_dis[e.to] = mf_dis[v] + 1;
                q.push(e.to);
            }
        }
    }
}

int mf_dfs(int v, int t, int f) {//通过mf_dfs寻找增广路
    if (v == t) return f;
    for (int &i = mf_cur[v]; i < mf_G[v].size(); i++) {
        edge &e = mf_G[v][i];
        if (e.cap > 0 && mf_dis[v] < mf_dis[e.to]) {
            int d = mf_dfs(e.to, t, min(f, e.cap));
            if (d > 0) {
                e.cap -= d;
                mf_G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

int maxflow(int s, int t) {
    int flow = 0;
    for (;;) {
        mf_bfs(s); //计算层次图
        if (mf_dis[t] < 0) return flow; //找不到s-t路径
        memset(mf_cur, 0, sizeof(mf_cur)); //初始化当前弧
        int f;
        while ((f = mf_dfs(s, t, inf)) > 0) {  //更新最大流
            flow += f;
        }
    }
    return flow;
}

void init() {
    mf_init();
}

void sol() {
    init();
    _rep(i, 1, n) scanf("%s", s[i] + 1);
    int cnt = 0;
    _rep(i, 1, n) {
        int pos = 1;
        while(pos <= n) {
            while(pos <= n && s[i][pos] != '.') ++pos;
            if(pos > n) continue;
            ++cnt;
            while(pos <= n && s[i][pos] == '.') num[i][pos++] = cnt; 
        }
    }
    int S = 0, tem = cnt;
    _rep(i, 1, cnt) mf_addedge(S, i, 1);
    _rep(i, 1, n) {
        int pos = 1;
        while(pos <= n) {
            while(pos <= n && s[pos][i] != '.') ++pos;
            if(pos > n) continue;
            ++cnt;
            while(pos <= n && s[pos][i] == '.') mf_addedge(num[pos++][i], cnt, 1);
        }
    }
    int T = cnt + 1;
    _rep(i, tem + 1, cnt) mf_addedge(i, T, 1);
    printf("%lld\n", maxflow(S, T));
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    while(n = read(), n) {
        sol();
    }

    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}
二分图匹配-匈牙利算法
// #pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 105;
const int maxm = 25;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
char s[maxn][maxn];
int num[maxn][maxn];
int vis[maxn], matching[maxn];
vector <int> G[maxm];

int dfs(int u) {
    for(int &v : G[u]) {
        if(!vis[v]) {
            vis[v] = 1;
            if(!matching[v] || dfs(matching[v])) {
                matching[v] = u;
                return 1;
            }
        }
    }
    return 0;
}

int hungarian(int cnt) {
    int ans = 0;
    _rep(i, 1, cnt) {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ++ans;
    }
    return ans;
}

void init() {
    for(int i = 0; i < maxn; i++) G[i].clear();
    memset(matching, 0, sizeof(matching));
}

void sol() {
    init();
    _rep(i, 1, n) scanf("%s", s[i] + 1);
    int cnt = 0;
    _rep(i, 1, n) {
        int pos = 1;
        while(pos <= n) {
            while(pos <= n && s[i][pos] != '.') ++pos;
            if(pos > n) continue;
            ++cnt;
            while(pos <= n && s[i][pos] == '.') num[i][pos++] = cnt; 
        }
    }
    int tem = cnt;
    _rep(i, 1, n) {
        int pos = 1;
        while(pos <= n) {
            while(pos <= n && s[pos][i] != '.') ++pos;
            if(pos > n) continue;
            ++cnt;
            while(pos <= n && s[pos][i] == '.') G[num[pos++][i]].push_back(cnt);
        }
    }
    printf("%lld\n", hungarian(tem));
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    while(n = read(), n) {
        sol();
    }

    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}

The Accomodation of Students(二分图匹配)

OJ:HDUOJ 2444
VJudge

题意

有若干对彼此认识的学生。你的任务是将学生分成两组,以便同一组中的任何两个学生都不认识。如果可以实现此目标,则将他们安排在双人间。注意只有彼此认识的学生才能住在同一房间。

计算安排到双人间的对数或者判断出无法分成两组。

题解

首先考虑将学生分成两组,可以采用 D F S DFS DFS 染色的方式进行,每隔一个人染成相同颜色,相同颜色的人分到一组。如果中间遇到认识的两个人颜色一样就说明无法完成分组。

染完色之后就可以跑匈牙利算法了。

代码

// #pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 205;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct poi {

};

int vis[maxn], matching[maxn];
vector<int> G[maxn];
int n, m;
int col[maxn];

int dfs(int u) {
    for(int &v : G[u]) {
        if(!vis[v]) {
            vis[v] = 1;
            if(matching[v] == -1 || dfs(matching[v])) {
                matching[v] = u;
                return 1;
            }
        }
    }
    return 0;
}

int hungarian(int cnt) {
    int ans = 0;
    memset(matching, -1, sizeof(matching));
    _rep(i, 1, cnt) {
        memset(vis, 0, sizeof(vis));
        if(col[i] == 1 && dfs(i)) ++ans;
    }
    return ans;
}

int getColor(int u, int f) {
    col[u] = (f ? 1 : 2);
    for(int &v : G[u]) {
        if(!col[v] && getColor(v, f ^ 1) == 0 || col[v] == (f ? 1 : 2)) return 0;
    }
    return 1;
}

void init() {
    mem0(col);
    _rep(i, 1, n) G[i].clear();
}

int sol() {
    init();
    _for(i, m) {
        int u = read(), v = read();
        G[u].push_back(v);
        G[v].push_back(u);
    }
    _rep(i, 1, n) if(!col[i] && getColor(i, 0) == 0) return 0;
    printf("%d\n", hungarian(n));
    return 1;
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    while(cin>>n>>m) {
        if(!sol()) printf("No\n");
    }

    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}

Courses(裸二分图匹配)

OJ:hduoj 1083
vjudge

题意

n n n 门课程, m m m 个学生,每个学生喜欢若干门课。你可以从一个学生喜欢的课程里选出一门课,让该学生担任课代表。

问能不能找出 n n n 个不同的学生担任 n n n 门课的课代表。

题解

直接建图跑匈牙利算法。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 405;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct poi {

};

int n, m;
vector<int> G[maxn];

void init() {
    _rep(i, 1, n + m) G[i].clear();
}

/*
@param cnt: 二分图左边点集的编号最大值
int hungarian(int cnt)
*/
int vis[maxn], matching[maxn];
int dfs(int u) {
    for(int &v : G[u]) {
        if(!vis[v]) {
            vis[v] = 1;
            if(matching[v] == -1 || dfs(matching[v])) {
                matching[v] = u;
                return 1;
            }
        }
    }
    return 0;
}
int hungarian(int cnt) {
    int ans = 0;
    memset(matching, -1, sizeof(matching));
    _rep(i, 1, cnt) {
        memset(vis, 0, sizeof(vis));
        if(dfs(i)) ++ans;
    }
    return ans;
}

void sol() {
    init();
    _rep(i, 1, n) {
        int num = read();
        _for(j, num) G[i].push_back(read() + n);
    }
    printf("%s\n", hungarian(n) == n ? "YES":"NO");
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    int T = read();
    _for(i, T) {
        n = read(), m = read();
        sol();
    }

    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值