【题解】试题库问题

版权声明:Powered By Fighter https://blog.csdn.net/qq_30115697/article/details/88794864

题意:

原题传送门

思路

按照最大流的日常套路,我们来考虑如何建图。

显然我们把题的类型和每套试题分别作为图中的点。用编号为1~n的点表示n套试题,编号为n+1~n+k的点表示k种类型。

由于每套试题贡献为1,那么我们就从试题向它所属的所有类型连权值为1的边,从源点向试题连权值为1的边。然后,因为每种试题需要给定的 v i v_i 个,那么我们就从每种类型向汇点连权值为 v i v_i​ 的边,然后跑一遍最大流,那么我们就可以得到多套试题和一种类型的匹配。

如何判断是否有解呢?很简单,只需要判断最后得到的最大流量是否等于给定的 m m 即可。

方案怎么输出呢?我们考虑当一套试题被选择时,当前边的流量一定已经被清空,而反向边一定不为0.所以我们可以枚举试题类型,遍历与这个类型直接相连的且边权不为0的点,就是选择的试题。

代码

#include <bits/stdc++.h>
#define MAXN 2005
#define MAXM 400005
#define INF 0x3f3f3f3f
using namespace std;

int n, m, k, cnt = 1, s, t;
int head[MAXN], vet[MAXM], Next[MAXM], cost[MAXM];

void add(int x, int y, int w){
    cnt++;
    Next[cnt] = head[x];
    head[x] = cnt;
    vet[cnt] = y;
    cost[cnt] = w;
}

int dep[MAXN], vis[MAXN], cur[MAXN];
bool bfs(){
    for(int i = 1; i <= t; i++){
        dep[i] = INF;
        vis[i] = 0;
        cur[i] = head[i];
    }
    queue<int> q;
    q.push(s);
    vis[s] = true;
    dep[s] = 1;
    while(!q.empty()){
        int x = q.front();
        q.pop();
        vis[x] = false;
        for(int i = head[x]; i; i = Next[i]){
            int v = vet[i];
            if(cost[i] && dep[v] > dep[x]+1){
                dep[v] = dep[x]+1;
                if(!vis[v]){
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    return dep[t] != INF;
}

int ans;

int dfs(int x, int flow){
    if(x == t){
        ans += flow;
        return flow;
    }
    int used = 0;
    for(int i = head[x]; i; i = Next[i]){
        int v = vet[i];
        if(cost[i] && dep[v] == dep[x]+1){
            int m = dfs(v, min(flow-used, cost[i]));
            if(m){
                cost[i] -= m;
                cost[i^1] += m;
                used += m;
                if(used == flow) break;
            }
        }
    }
    return used;
}

void dinic(){
    while(bfs()){
        dfs(s, INF);
    }
}

int main()
{
    cin >> k >> n;
    s = 0, t = n*2+1;
    int p, x;
    for(int i = 1; i <= k; i++){
        scanf("%d", &x);
        m += x;
        add(i+n, t, x);
        add(t, i+n, 0);
    }
    for(int i = 1; i <= n; i++){
        add(s, i, 1);
        add(i, s, 0);
        scanf("%d", &p);
        for(int j = 1; j <= p; j++){
            scanf("%d", &x);
            add(i, x+n, 1);
            add(x+n, i, 0);
        }
    }
    dinic();
    if(ans != m){
        puts("No Solution!");
        return 0;
    }
    for(int i = 1; i <= k; i++){
        printf("%d: ", i);
        for(int j = head[i+n]; j; j = Next[j]){
            int v = vet[j];
            if(cost[j] && v <= n){
                printf("%d ", v);
            }
        }
        putchar('\n');
    }
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/88794864