攻略世界树 (网络流)

【问题描述】

这是在ALO世界线上。为了帮助桐子救出本子娜,ALfheim Online的多个工会展开了针对世界树的攻略活动。

但是在攻略之前,必须策划好进攻方案。策划进攻方案之前也要先完成基础人员分组。

在ALO中的小组分配中大致有四种定位,队长,战士,牧师,法爷。一个标准的小队应当拥有这四种人至少每种各一个。

但是实际情况往往不如人意,人员职业往往并非正好1:1:1:1(就好像电院的男女比例不会低于7:1一样),

目前总共有h个队长(Hero), w个战士(Warrior), c个牧师(Claric), m个法爷(Mage)。

所以允许最多有nw个小队没有战士,nc个小队没有牧师,nm个小队没有法爷。(没有队长还叫小队么……)

但是,队伍分配也不是乱分的。你总不能让队长单干吧。

如果一个小队没有牧师,那么这个小队至少要有战士和法爷。(也就是说小队最少两人,必须一队长一牧师。反正只要奶着队长不死就行了很合理嘛www。)

最后麻烦的来了。虽然大家都是来帮桐子救本子娜的,但是大家到底来自不同地区不同种族,随便组到一起还打毛线(比如众所周知光属性的治疗魔法对暗属性有伤害233……黑暗精灵哭瞎)。所以一个小队里面的人必须还算合得来才行。

人这么多,一个小队里的人全都合得来不太科学。现在只要求职业关系最紧密的合得来就够了。

也就是说,同一个小队里的战士必须和队长合得来,牧师必须和战士合得来,法爷必须和牧师合得来。非常合理是不是,站位最近,职业契合度高,这要是不合得来怎么打。(Q:要是一个队只有两个人,但是队长是黑暗精灵,牧师却是光属性治疗怎么玩?A:朔夜因为某些部位脂肪过多导致无脑,你按她说的分配就行了。Q:……)

风精灵族长朔夜将这个艰巨的任务交给了你。她希望你分的小组数尽量多。并且她还警告你说如果一秒钟内做不出来,本子娜就要被诗乃NTR了,到底这年头后宫不如百合啊。

 

【输入】

    多组数据,每组数据的开头是七个非负整数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~好吧好像也没啥用


下面才是题解:

觉得自己现在天天被虐啊~~~每天写暴力的日常我也是醉了= =。对,你没有看错,这道题就是萌萌哒的(shui)网络流= =。

网络流的题的瓶颈就是建图啊。还是来讲讲如何建图吧。

将原题的h,w,c,m分别改为A,B,C,D来表示,将nw,nc,nm改为nb,nc,nd来表示。加入超级源点S,和汇点T。


首先,将B,C,D三种人员拆点,对于有依赖关系的人员连边。
然后对于只选A和C的,或者只选B的,我们可以新加入六个节点,分别用tran1,tran2,tran3,tran4,tran5,tran6表示。其中,将tran1与tran2连边,容量为nb,将tran3和tran4连边,容量为nd,将tran5和tran6连边,容量为nc。然后我们将A的每一个节点与tran1相连,将tran2与C的每一个入点相连。将tran3与C的每一个出点相连,每一条边容量都为1。再将tran4与汇点T相连,容量为inf。这样就表示了只含有队长和法师的情况(A和C)。

再将B的每一个出点与tran5相连,将tran6与D的每一个入点相连。这样就表示了只含有队长,战士,和法爷的情况(A和B和D)。最后将源点S与每个A相连,将D的每一个出点与T相连。建图就完成了。这样就只用跑dinic即可。


你以为建图完成了程序就没问题了吗?我今天下午调了好久,还一直单纯的认为是自己的图建错了,最后才发现是自己dinic手抖打错了一个判断。想想世界怎会有我等愚蠢之人,我还真是too young too native。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;

const int maxn = 1000;
const int inf = (int)1e7;

int A, B, C, D, nb, nc, nd;
int N;
int S, T, tran1, tran2, tran3, tran4, tran5, tran6;//Transit

int head[maxn];
int  dis[maxn];

struct node{
	int v, c, next;
}e[500000];

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 k = 0;
void adde(int u, int v, int cal){
	e[k].v = v; e[k].c = cal; e[k].next = head[u]; head[u] = k++;
	e[k].v = u; e[k].c = 0;   e[k].next = head[v]; head[v] = k++;
}

bool bfs(){
	memset(dis, -1, sizeof(dis));
	queue <int> q;
	dis[S] = 0; q.push(S);
	while(!q.empty()){
		int u = q.front(); q.pop();
		for(int i = head[u]; i != -1; i = e[i].next){
			int v = e[i].v;
			if(dis[v] == -1 && e[i].c > 0){
				dis[v] = dis[u] + 1; q.push(v);
			}
		}
	}
	return dis[T] != -1;
}

int dfs(int u, int k){
	if(u == T)return k;
	int ret = 0;
	for(int i = head[u]; i != -1; i = e[i].next){
		int v = e[i].v;
		if(dis[v] == dis[u] + 1){
			int t = dfs(v, min(k - ret, e[i].c));
			e[i].c -= t; e[i ^ 1].c += t; ret += t;
			if(ret == k)return k;
		}
	}
	if(ret == 0)dis[u] = -1;
	return ret;
}

void dinic(){
	int ret = 0;
	while(bfs()){
		ret += dfs(S, inf);
	}
	printf("%d\n", ret);
}

int main(){
	while(1){
		memset(head, -1, sizeof(head));
		
		//input
		A = read(); B = read(); C = read(); D = read();
		nb = read(); nc = read(); nd = read();
		if(A == -1 && B == -1 && C == -1 && D == -1 && nb == -1 && nc == -1 && nd == -1)break;
		
		//init
		S = 0; T = 2 * (A + B + C + D) + 10;
		N = A + B + C + D;//the total.
		tran1 = N * 2 + 1; tran2 = N * 2 + 2;
		tran3 = N * 2 + 3; tran4 = N * 2 + 4;
		tran5 = N * 2 + 5; tran6 = N * 2 + 6;
		
		adde(tran1, tran2, nb);// A -> C
		adde(tran3, tran4, nd);// C -> T
		adde(tran5, tran6, nc);// B -> D
//====================================================================
		for(int i = 1; i <= A; ++i){//the end of A -> tran1
			adde(i, tran1, 1);
		}
		for(int i = 1; i <= C; ++i){//tran2 -> the start of C
			adde(tran2, A + B + i, 1);
		}
		
		for(int i = 1; i <= C; ++i){//the end of C -> tran3
			adde(N + A + B + i, tran3, 1);
		}
		adde(tran4, T, inf);
		
		for(int i = 1; i <= B; ++i){//the end of B -> tran5
			adde(N + A + i, tran5, 1);
		}
		for(int i = 1; i <= D; ++i){//tran6 -> the start of D
			adde(tran6, A + B + C + i, 1);
		}
		
//====================================================================
		for(int i = 1; i <= B; ++i){//for B.
			int ni = read();
			for(int j = 1; j <= ni; ++j){
				int t = read(); adde(t, i + A, 1);
			}
			adde(A + i, N + A + i, 1);
		}
		
		for(int i = 1; i <= C; ++i){//for C.
			int ni = read();
			for(int j = 1; j <= ni; ++j){
				int t = read(); adde(t + N + A, i + A + B, 1);//the end of B -> the start of C.
			}
			adde(A + B + i, N + A + B + i, 1);
		}
		
		for(int i = 1; i <= D; ++i){//for D.
			int ni = read();
			for(int j = 1; j <= ni; ++j){
				int t = read(); adde(t + N + A + B, i + A + B + C, 1);//the end of C -> the start of D.
			}
			adde(A + B + C + i, N + A + B + C + i, 1);
		}
//====================================================================
		for(int i = 1; i <= A; ++i){//link the start and A.
			adde(S, i, 1);
		}
		for(int i = 1; i <= D; ++i){//link the end and D.
			adde(N + A + B + C + i, T, 1);
		}
		
		dinic();
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值