【最大流】攻略世界树!

【描述】

有四种定位,队长,战士,牧师,法爷。一个标准的小队应当拥有这四种人至少每种各一个。

目前总共有h个队长(Hero), w个战士(Warrior), c个牧师(Claric), m个法爷(Mage)。
允许最多有nw个小队没有战士,nc个小队没有牧师,nm个小队没有法爷。
如果一个小队没有牧师,那么这个小队至少要有战士和法爷。
同一个小队里的战士必须和队长合得来,牧师必须和战士合得来,法爷必须和牧师合得来。
求分组组数最大值。


【输入】

多组数据,每组数据的开头是七个非负整数h, w, c, m, nw, nc, nm。
接下来w行,每行开头一个数ni意味着战士中的第i人可以和ni个人合得来,然后接ni个数字,意味着他具体可以和队长中的第几人合得来。
接下c行就是对牧师的描述,代表每个牧师可以和哪些战士合得来;
再接下m行,代表每个法爷可以和哪些牧师合得来。
数据以7个-1结束,不要对这一行输出结果。


【输出】

对每组数据一行,一行一个数字,代表最多能组出多少个小队。


【样例输入】

2 1 1 1 1 1 1
1 1
1 1
1 1
1 1 1 1 0 0 0
1 1
1 1
1 1
1 0 1 0 1 0 1
0
1 1 0 1 0 1 0
0
0
1 1 0 1 0 1 0
1 1
0
-1 -1 -1 -1 -1 -1 -1


【样例输出】

2
1
1
0
1


【数据范围】

每个文件中的数据组数不超过10组。
30%的数据:每组数据第一行的七个数字均不超过5。
70%的数据:每组数据第一行的七个数字均不超过30。
100%的数据:每组数据第一行的七个数字均不超过50。

输入有少量不确定的空格和换行用以增大尝试使用读入优化的人所付出的成本www~好吧好像也没啥用


【Code】

其实就是存个代码……挺裸的拆点网络流啊什么的…………

这代码竟然都有4k。。。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int Nmax = (50 + 15) * 8;
const int Mmax = Nmax * Nmax * 2;
const int inf = 0x3f3f3f3f;

int h, w, c, m, nw, nc, nm;
struct ed{
    int v, flow, next;
}e[Mmax];
int k, head[Nmax], cur[Nmax];
int S, T;
int innw, outnw, innc, outnc, innm, outnm;

inline void adde(int u, int v, int flow)
{
	//printf("%d -> %d : %d\n", u, v, flow);
    e[k] = (ed) { v, flow, head[u] };
    head[u] = k++;
    e[k] = (ed) { u, 0, head[v] };
    head[v] = k++;
}

int dis[Nmax];
queue <int> q;

bool bfs()
{
    memcpy(cur, head, sizeof(cur));
    memset(dis, -1, sizeof(dis));
    while (q.size()) q.pop(); q.push(S); dis[S] = 0;
    while (q.size()) {
        int u = q.front(); q.pop();
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].v;
            if (e[i].flow && dis[v] == -1) {
                dis[v] = dis[u] + 1;
                if (v == T) return true;
                q.push(v);
            }
        }
    }
    return false;
}

int dfs(int u, int maxf)
{
    if (u == T || !maxf) return maxf;
    int flow = 0, f;
    for (int &i = cur[u]; i; i = e[i].next) {
        int v = e[i].v;
        if (dis[v] == dis[u] + 1 && (f = dfs(v, min(maxf, e[i].flow))) > 0) {
            e[i].flow -= f; e[i ^ 1].flow += f;
            flow += f; maxf -= f; if(!maxf) break;
        }
    }
    return flow;
}

int max_flow()
{
    int f = 0;
    while (bfs()) f += dfs(S, inf);
    return f;
}

inline int inw(int x) { return x + h; }
inline int outw(int x) { return x + h + w; }
inline int inc(int x) { return x + h + 2 * w; }
inline int outc(int x) { return x + h + 2 * w + c; }
inline int inm(int x) { return x + h + 2 * w + 2 * c; }
inline int outm(int x) { return x + h + 2 * w + 2 * c + m; }

int main()
{
	freopen("attackonworldtree.in", "r", stdin);
	freopen("attackonworldtree.out", "w", stdout);
	
    ios :: sync_with_stdio(false);
    while (cin >> h >> w >> c >> m >> nw >> nc >> nm) {
    	if(!(~h)) break;
    	memset(head, 0, sizeof(head)); k = 2;
    	S = 0, innw = h + 2 * w + 2 * c + 2 * m + 1;
	    outnw = innw + 1; innc = outnw + 1; outnc = innc + 1;
	    innm = outnc + 1; outnm = innm + 1; T = outnm + 1;
	    
	    for (int i = 1; i <= h; ++i) {
	        adde(S, i, 1);
	        adde(i, innw, inf);
	    }
	    for (int i = 1; i <= w; ++i) {
	        int cnt, u; cin >> cnt;
	        while (cnt--) {
	            cin >> u;
	            adde(u, inw(i), 1);
	        }
	        adde(inw(i), outw(i), 1);
	        adde(outw(i), innc, inf);
	    }
	    for (int i = 1; i <= c; ++i) {
	        int cnt, u; cin >> cnt;
	        while (cnt--) {
	            cin >> u;
	            adde(outw(u), inc(i), 1);
	        }
	        adde(outnw, inc(i), inf);
	        adde(inc(i), outc(i), 1);
	        adde(outc(i), innm, inf);
	    }
	    for (int i = 1; i <= m; ++i) {
	        int cnt, u; cin >> cnt;
	        while (cnt--) {
	            cin >> u;
	            adde(outc(u), inm(i), 1);
	        }
	        adde(outnc, inm(i), inf);
	        adde(inm(i), outm(i), 1);
	        adde(outm(i), T, 1);
	    }
	    
	    adde(outnm, T, inf); adde(innw, outnw, nw);
	    adde(innc, outnc, nc); adde(innm, outnm, nm);
	    
	    cout << max_flow() << endl;
    }
    
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值