[acm/icpc2016北京赛区][hihocoder1424] Asa's Chess Problem 有上下界的费用流

将行列分开来考虑

对于每一组交换,若对应点同色那么这组交换是没有意义的。我们只考虑两组交换是不同色的

建图

1、超级源连向所有的行和列,上界=下界=原本处在这一行的点数,费用为0;

2、对于每组交换i,j,若i为黑色,那么点i连向j,上界为1下界为0,费用为1;

3、所有的行和列连向超级汇,上界和下界为读入的限制,费用为0;

可行流判断是否有解,若有解则最小费用即答案



#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define P 55
#define N 2050
#define INF (1<<30)

using namespace std;

int n,m,S,T,ss,tt,cnt=1,tot=0;
int head[N],d[N],p[N],qlc[P],qrc[P],qlr[P],qrr[P];
int map[P][P],sc[P],sr[P],r[P],c[P],dis[N];
int ans,tl;
struct Edge{ int a,b,v,cost,next; }e[10*N];
void add(int a,int b,int v,int cost) {
	e[++cnt].a = a;
	e[cnt].b = b;
	e[cnt].v = v;
	e[cnt].cost = cost;
	e[cnt].next = head[a];
	head[a] = cnt;
}

inline void add_Node(int a,int b,int vl,int vr,int cost) {
	//printf("Node %d %d %d %d\n",a,b,vl,vr);
	d[a] -= vl; d[b] += vl;
	add(a,b,vr-vl,cost); add(b,a,0,-cost);
}
void paul() {
	ss = tot+1; tt = tot+2;
	for (int _=1;_<=tot;_++) {
		if (d[_] < 0) add(_,tt,-d[_],0) , add(tt,_,0,0);
		if (d[_] > 0) add(ss,_, d[_],0) , add(_,ss,0,0); 
	}
}

#define cp e[i].v
#define B e[i].b

bool SPFA() {
	bool flag = false;
	memset(p,0,sizeof(p));
	memset(dis,127/3,sizeof(dis));
	dis[ss] = 0;
	queue<int> q; q.push(ss);
	while (!q.empty()) {
		int u = q.front(); q.pop();
		if (u == tt) flag = true; 
		for (int i=head[u];i;i=e[i].next)
			if (cp > 0 && dis[u] + e[i].cost < dis[B]) {
				dis[B] = dis[u] + e[i].cost;
				p[B] = i;
				q.push(B);
			} 
	}
	return flag;
}

void mcf() {
	int g = p[tt] , flow = INF;
	while (g) {
		flow = min(flow , e[g].v);
		g = p[ e[g].a ];
	}
	g = p[tt];
	while (g) {
		e[g  ].v -= flow;
		e[g^1].v += flow;
		ans += e[g].cost * flow;
		g = p[ e[g].a ];
	}
	tl += flow;
}

void init() {
	memset(head,0,sizeof(head));
	memset(e,0,sizeof(e));
	memset(d,0,sizeof(d));
	
	//memset(qlr,0,sizeof(qlr));
	//memset(qrr,0,sizeof(qrr));
	//memset(qlc,0,sizeof(qlc));
	//memset(qrc,0,sizeof(qrc));
	
	memset(sc,0,sizeof(sc));
	memset(sr,0,sizeof(sr));
	memset(c,0,sizeof(c));
	memset(r,0,sizeof(r));
	memset(map,0,sizeof(map));
	cnt = 1; tot = 0;
	S = ++tot; T = ++tot; add_Node(T,S,0,INF,0);
	ans = tl = 0;
}


int main() {	
	#ifndef ONLINE_JUDGE
		freopen("5.in","r",stdin);
	#endif
	while (~scanf("%d",&n)) {
		init();
		for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) {
			scanf("%d",&map[i][j]);
			if (map[i][j]) sr[i]++ ,sc[j]++;
		}
		for (int i=1;i<=n;i++) r[i] = ++tot , c[i] = ++tot;
		for (int i=1;i<=n;i++) {
			scanf("%d%d",&qlr[i],&qrr[i]);
			add_Node(S,r[i],sr[i],sr[i],0); //³¬¼¶Ô´ --> µÚiÐÐ   
			add_Node(r[i],T,qlr[i],qrr[i],0);       //µÚiÐÐ  --> ³¬¼¶»ã 
		}
		for (int i=1;i<=n;i++) {
			scanf("%d%d",&qlc[i],&qrc[i]);
			add_Node(S,c[i],sc[i],sc[i],0); //³¬¼¶Ô´ --> µÚiÁÐ 
			add_Node(c[i],T,qlc[i],qrc[i],0);       //µÚiÁÐ  --> ³¬¼¶»ã 
		} 
		for (int i=1;i<=n*n/2;i++) {
			int x1,y1,x2,y2;
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			if (map[x1][y1] == map[x2][y2]) continue;
			if (!map[x1][y1]) swap(x1,x2) , swap(y1,y2);
			if (x1 == x2) add_Node(c[y1],c[y2],0,1,1); //Èç¹ûÔÚͬһÐÐ ¾ÍÔÚÁÐÖ®¼äÁ¬±ß 
			if (y1 == y2) add_Node(r[x1],r[x2],0,1,1); //Èç¹ûÔÚͬһÁÐ ¾ÍÔÚÐÐÖ®¼äÁ¬±ß 
		} 
		paul(); //²¹ÉÏËùÓвðµÄ±ß 
		
		//for (int i=2;i<=cnt;i+=2) if (e[i].v) printf("%d %d %d %d\n",e[i].a,e[i].b,e[i].v,e[i].cost); 
			
		while (SPFA()) 
			mcf();
		//for (int i=3;i<=cnt;i+=2) if (e[i].v) printf("%d %d %d\n",e[i].b,e[i].a,e[i].v);
		
		for (int i=head[ss];i;i=e[i].next) if (e[i].v) ans = -1;//ÅжÏÊÇ·ñÓнâ
		if (ans == -1) puts("-1"); else printf("%d\n",ans);
	}
	return 0;
} 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值