HDU3395 Special Fish 费用流

最近没怎么写博客了, 果断是觉得以前写博客的自己好sb。。。

看到这么丧心病狂的题目背景, 突然就想写写了。。


题意:

有一群丧心病狂的莫名物种的鱼, 每个都认为自己是male( 特么地这样能科学?? ), 然后每条鱼都有一个val值, 并且有可能会攻击它认为是female的鱼, 更加丧心病狂的是攻击了之后会怀孕惊恐惊恐, 很想问下这是什么攻击。   怀孕之后会产下一个价值为两个父亲的价值XOR的后代, 每条鱼最多只能XX别人一次, 也最多只能被别人XX一次, 然后求产下后代val值的和的最大值。


思路:

将一个点拆成两个点(1~n和n+1~2n), 分别表示XX别人和被别人XX,如果i能XXj 就向i到j+n连一条费用为-val[i]^val[j], 流量为1的边,如果这条边有流量的话就表示iXXj,

源点S = 0, 汇点T = 2 * n + 1, 向S到i连费用为0, 流量为1的边, 向i+n到T连费用为0, 流量1的边。

然后求出最小费用流就可以了。 刚开始以为要满流, wa了几次之后才发现不用满流, 每次找到最短路更新的最小费用的时候顺便更新下答案就可以了。


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

#define mxn 302
#define mxe 100020
#define inf 0x3f3f3f3f


struct edge {
	int u, v, cap, flow, cost, nxt;
	edge() {}
	edge( int u, int v, int cap, int flow, int cost, int nxt ):
		u( u ), v( v ), cap( cap ), flow( flow ), cost( cost ), nxt( nxt ) {}
};
struct mcmf {
	edge e[mxe];
	int cc, fst[mxn], p[mxn], d[mxn], a[mxn];
	bool in[mxn];

	void init() {
		memset( fst, -1, sizeof( fst ) );
		cc = 0;
	}
	void add( int u, int v, int cap, int cost ) {
		e[cc] = edge( u, v, cap, 0, cost, fst[u] );
		fst[u] = cc++;
		e[cc] = edge( v, u, 0, 0, -cost, fst[v] );
		fst[v] = cc++;
	}

	int bf( int s, int t, int &mf, int &mc ) {
		memset( d, 0x3f, sizeof( d ) );
		memset( in, 0, sizeof( in ) );
		d[s] = 0, a[s] = inf, in[s] = 1, p[s] = 0;
		queue<int> q;
		q.push( s );
		while( !q.empty() ) {
			int x = q.front(); q.pop();
			in[x] = 0;
			for( int i = fst[x]; ~i; i = e[i].nxt ) {
				int v = e[i].v;
				if( e[i].cap > e[i].flow && d[v] > d[x] + e[i].cost ) {
					d[v] = d[x] + e[i].cost;
					p[v] = i;
					a[v] = min( a[x], e[i].cap - e[i].flow );
					if( !in[v] )
						in[v] = 1, q.push( v );
				}
			}
		}
		if( d[t] == inf )
			return 0;
		mf += a[t];
		mc += a[t] * d[t];
		int u = t;
		while( u != s ) {
			e[p[u]].flow += a[t];
			e[p[u]^1].flow -= a[t];
			u = e[p[u]].u;
		}
		return 1;
	}

	int go( int s, int t ) {
		int res = 0;
		int mf = 0, mc = 0;
		while( bf( s, t, mf, mc ) ) {
			res = min( res, mc );
		}
		return res;
	}
}go;


int n, a[mxn], S, T;
char s[mxn];
int main() {
	while( scanf( "%d", &n ) != EOF && n ) {
		S = 0, T = 2 * n + 1;
		for( int i = 1; i <= n; ++i )
			scanf( "%d", &a[i] );
		go.init();
		for( int i = 1; i <= n; ++i ) {
			scanf( "%s", s + 1 );
			for( int j = 1; j <= n; ++j )
				if( s[j] == '1' )
					go.add( i, j + n, 1, - ( a[i] ^ a[j] ) );
			go.add( S, i, 1, 0 );
			go.add( i + n, T, 1, 0 );
		}
		printf( "%d\n", - go.go( S, T ) );
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值