[Baltic2008]Mafia

27 篇文章 0 订阅

题目

传送门 to BZOJ

题目概要
n n n 个点、 m m m 条边的无向图,找一些点,使得删去这些点后, a a a b b b 不连通。最小化点权和。

数据范围与约定
2 ≤ n ≤ 200 , 1 ≤ m ≤ 20000 2\le n\le 200,1\le m\le 20000 2n200,1m20000

思路

错误思路:动态规划

什么?万能的DP竟然是错误思路?

f ( x ) f(x) f(x) 表示 x x x 不可达的最小费用。

满心以为, f ( x ) = min ⁡ [ c x , ∑ ⟨ x , i ⟩ ∈ E f ( i ) ] f(x)=\min\left[c_x,\sum_{\langle x,i\rangle\in E}f(i)\right] f(x)=min[cx,x,iEf(i)]

然而……我们并没有规定 f ( x ) f(x) f(x) 的定义域在哪里!

所以,一个点可能被 f ( y 1 ) , f ( y 2 ) f(y_1),f(y_2) f(y1),f(y2) 各计算一次。于是……稻花香里说丰年,听取 w a \tt wa wa 声一片……

如果你愿意,可以改成状压。

正确思路:网络流

似乎就是把这张图切开。那就求 最小割 吧!

如果我们控制了一个点,那么劫匪就“有进无回”。所以我们把一个点的入度、出度承载在两个点上,两个点之间的连边是点权。切掉这条边,就相当于“进去了就出不来了”。

然后求就行了。说起来, O ( V 2 E ) \mathcal O(V^2E) O(V2E) 的算法都能过?我用的 i s a p \tt{isap} isap

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
inline int readint(){ int x; scanf("%d",&x); return x; }
inline void writeint(long long x){ printf("%lld",x); }

const int MaxN = 505, infty = (1<<30)-1;
int c[MaxN][MaxN], n, m;

void addEdge(int from,int to,int val){
	c[from][to] += val;
}
int started, ended;
void input(){
	n = readint(), m = readint();
	started = readint()-1, ended = readint()-1;
	for(int i=0; i<n; ++i)
		addEdge(i<<1,i<<1|1,readint());
	for(int i=0,u,v; i<m; ++i){
		u = readint()-1, v = readint()-1;
		addEdge(u<<1|1,v<<1,infty);
		addEdge(v<<1|1,u<<1,infty);
	}
}

int d[MaxN], vd[MaxN];
int dfs(int x,int inFlow,const int &T){
	if(x == T) return inFlow;
	int sum = 0, minD = (n<<1)-1, delta;
	for(int i=0; i<(n<<1); ++i)
		if(c[x][i] > 0){
			if(d[x] == d[i]+1){
				delta = dfs(i,min(inFlow-sum,c[x][i]),T);
				c[x][i] -= delta, c[i][x] += delta;
				if((sum += delta) == inFlow) break;
				if(d[T] == -1) return sum; /* over */
			}
			minD = min(minD,d[i]);
		}
	if(not sum){ /* 在G_f中没有出度 */
		if((-- vd[d[x]]) == 0) d[T] = -1;
		++ vd[d[x] = minD + 1];
	}
	return sum;
}
int isap(int s,int t){
	int maxFlow = 0;
	for(int i=0; i<(n<<1); ++i)
		d[i] = vd[i] = 0;
	for(vd[0]=(n<<1); ~d[t]; )
		maxFlow += dfs(s,infty,t);
	return maxFlow;
}

void solve(){
	writeint(isap(started<<1,ended<<1|1));
	putchar('\n');
}

int main(){
	input(), solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值