点击打开链接
题目:在一个N * M 的矩阵草原上,分布着羊和狼,每个格子只能存在0或1只动物。现在要用栅栏将所有的狼和羊分开,问怎么放,栅栏数放的最少,求出个数?
解析:将狼群看作一个集合,羊群看作一个集合。然后设置源点和汇点,将两点至存在动物的点的距离赋值为1,构图,由于求得是栅栏数,从存在动物的位置向四周发散点赋值为1,即该方向放置一个栅栏。然后可以发现变成了求最小割,即求出最大流。需要注意的是,由于数据比较大,200 * 200。如果设置源点和汇点相差较大(即s = 0,e = n * m ),容易TLE.
#include
#include
#include
using namespace std;
// 最大流 ISAP + bfs+栈优化
const int maxn = 100010; //点的个数
const int maxm = 400010; //边的个数
const int INF = 0xfffffff;
struct Edge{
int to, next, cap, flow;
}edge[ maxm ];//注意是maxm
int tol;
int head[ maxn ];
int gap[ maxn ], dep[ maxn ], cur[ maxn ];
void init(){
tol = 0;
memset( head, -1, sizeof( head ) );
}
void addedge( int u, int v, int w, int rw = 0 ){
edge[ tol ].to = v; edge[ tol ].cap = w; edge[ tol ].flow = 0;
edge[ tol ].next = head[ u ]; head[ u ] = tol++;
edge[ tol ].to = u; edge[ tol ].cap = rw; edge[ tol ].flow = 0;
edge[ tol ].next = head[ v ]; head[ v ] = tol++;
}
int Q[ maxn ];
void BFS( int start, int end ){
memset( dep, -1, sizeof( dep ) );
memset( gap, 0, sizeof( gap ) );
gap[ 0 ] = 1;
int front = 0, rear = 0;
dep[ end ] = 0;
Q[ rear++ ] = end;
while( front != rear ){
int u = Q[ front++ ];
for( int i = head[ u ]; i != -1; i = edge[ i ].next ){
int v = edge[ i ].to;
if( dep[ v ] != -1 )
continue;
Q[ rear++ ] = v;
dep[ v ] = dep[ u ] + 1;
gap[ dep[ v ] ]++;
}
}
}
int S[ maxn ];
int sap( int start, int end, int N ){
BFS( start, end );
memcpy( cur, head, sizeof( head ) );
int top = 0;
int u = start;
int ans = 0;
while( dep[ start ] < N ){
if( u == end ){
int Min = INF;
int inser;
for( int i = 0; i < top; ++i ){
if( Min > edge[ S[ i ] ].cap - edge[ S[ i ] ].flow ){
Min = edge[ S[ i ] ].cap - edge[ S[ i ] ].flow;
inser = i;
}
}
for( int i = 0; i < top; ++i ){
edge[ S[ i ] ].flow += Min;
edge[ S[ i ] ^ 1 ].flow -= Min;
}
ans += Min;
top = inser;
u = edge[ S[ top ] ^ 1 ].to;
continue;
}
bool flag = false;
int v;
for( int i = cur[ u ]; i != -1; i = edge[ i ].next ){
v = edge[ i ].to;
if( edge[ i ].cap - edge[ i ].flow && dep[ v ] + 1 == dep[ u ] ){
flag = true;
cur[ u ] = i;
break;
}
}
if( flag ){
S[ top++ ] = cur[ u ];
u = v;
continue;
}
int Min = N;
for( int i = head[ u ]; i != -1; i = edge[ i ].next ){
if( edge[ i ].cap - edge[ i ].flow && dep[ edge[ i ].to ] < Min ){
Min = dep[ edge[ i ].to ];
cur[ u ] = i;
}
}
gap[ dep[ u ] ]--;
if( !gap[ dep[ u ] ] )
return ans;
dep[ u ] = Min + 1;
gap[ dep[ u ] ]++;
if( u != start )
u = edge[ S[ --top ] ^ 1 ].to;
}
return ans;
}
int dir[ ][ 2 ] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
int main(){
int N, M, Case = 1;
while( scanf( "%d%d", &N, &M ) != EOF ){
int s = N * M, t = N * M + 1;
int n = N * M + 2, temp;
init();
for( int i = 0; i < N; ++i ){
for( int j = 0; j < M; ++j ){
scanf( "%d", &temp );
if( temp == 2 ){
addedge( s, M * i + j, INF );
}
if( temp == 1 ){
addedge( M * i + j, t, INF );
}
for( int k = 0; k < 4; ++k ){
int x = i + dir[ k ][ 0 ];
int y = j + dir[ k ][ 1 ];
if( x >= 0 && x < N && y >= 0 && y < M )
addedge( M * i + j, M * x + y, 1 );
}
}
}
printf( "Case %d:\n%d\n", Case++, sap( s, t, n ) );
}
return 0;
}