1565: [NOI2009]植物大战僵尸
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1565
Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 2508 Solved: 1147
[Submit][Status][Discuss]
Description
Input
小学回忆
Output
仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。
Sample Input
3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0
Sample Output
25
HINT
在样例中, 植物P1,1可以攻击位置(0,0), P2, 0可以攻击位置(2,1)。
一个方案为,首先进攻P1,1, P0,1,此时可以攻击P0,0 。共得到能源收益为(-5)+20+10 = 25。注意, 位置(2,1)被植物P2,0保护,所以无法攻击第2行中的任何植物。
【大致数据规模】
约20%的数据满足1 ≤ N, M ≤ 5;
约40%的数据满足1 ≤ N, M ≤ 10;
约100%的数据满足1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000 。
注:样例表示的数据并非按行列输入。
我们发现,一株植物必须被吃才能干掉被他所保护的植物和他左边的植物,因此,我们很容易会想到拓扑序,而有环的植物及其保护的植物事无论如何都无法吃掉的,删去这些点,就是一道最大权闭合子图的裸题了。
这道题是我刚学“Hellow World!”就在网上看到的题
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pa pair<int,int>
#define INF 1000000000
#define ll long long
#define p(x,y) (x-1)*m+y
using namespace std;
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 n,m,T;
int tot,top,cnt,ans;
int v[605],cur[605],h[605],in[605],q[605];
bool del[605];
struct edge{
int to,next,v;
}e[400005],ed[800005];int head[605],hed[605];
void adde( int u, int v ){
in[v]++;
e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
}
void adde2( int u, int v, int w ){
ed[++cnt].to=v; ed[cnt].next=hed[u]; hed[u]=cnt; ed[cnt].v=w;
ed[++cnt].to=u; ed[cnt].next=hed[v] ;hed[v]=cnt; ed[cnt].v=0;
}
void dfs( int x ){
del[x] = 1;
for( int i = head[x]; i; i = e[i].next )
if( !del[e[i].to] ) dfs(e[i].to);//有环植物保护的任何一株植物都不能打
}
void topsort() {
for( int i = 1; i <= m*n; i++ )
if( !in[i] ) q[++top] = i;
else del[i] = 1;
while( top ) {
int now = q[top];del[now] = 0;//清除标记
top--;
for( int i = head[now]; i; i = e[i].next) {
in[e[i].to]--;
if( !in[e[i].to] ) q[++top] = e[i].to;
}
}
for( int i = 1; i <= n*m; i++ )
if( del[i] ) dfs( i );//始终有入度,说明有环
}
void rebuild() {
cnt = 1;T = n*m+1;
for( int x = 1; x <= n*m; x++ ) {
if( !del[x] ) {//由最大权闭合子图转网络流 并用最小割
if( v[x] > 0 ) tot += v[x], adde2( x, T, v[x] );
else adde2( 0, x, -v[x] );
for( int i = head[x]; i; i = e[i].next )
if( !del[e[i].to] ) adde2( x, e[i].to, INF );
}
}
}
bool bfs(){//是否割完
int hd = 0;int tail = 1;
for( int i = 1; i <= T; i++ ) h[i] = -1;
h[0] = 0;q[0] = 0;
while( hd != tail ) {
int now = q[hd];hd++;
for( int i = hed[now]; i; i = ed[i].next )
if( ed[i].v && h[ed[i].to] == -1 )
h[ed[i].to] = h[now]+1,q[tail++] = ed[i].to;
}
return h[T] != -1;
}
int dfs( int x, int f ){
if( x == T ) return f;
int w, used = 0;
for( int i = cur[x]; i; i = ed[i].next)
if( h[ed[i].to] == h[x] + 1 ) {
w = dfs(ed[i].to,min(ed[i].v,f-used));
ed[i].v -= w; ed[i^1].v += w;
if( ed[i].v ) cur[x] = i;
used += w; if( used == f ) return f;
}
if( !used ) h[x] = -1;
return used;
}
void dinic(){
while( bfs( ) ) {
for( int i = 0; i <= T; i++ ) cur[i] = hed[i];
ans += dfs( 0, INF );
}
}
int main(){
n = read();m = read();
for( int i = 1; i <= n; i++ )
for( int j = 1; j <= m; j++ ){
v[p(i,j)] = read();
int x = read();
while( x-- ){
int a = read(), b = read();
a++; b++;
adde( p(i,j), p(a,b) ); //保护关系
}
}
for( int i = 1; i <= n; i++ )
for( int j = m; j > 1; j-- )
adde( p(i,j), p(i,j-1) );//相当于右边的植物保护左边的植物
topsort();
rebuild();
dinic();
printf( "%d", tot-ans );
return 0;
}