1565: [NOI2009]植物大战僵尸
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 2345 Solved: 1081
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0
Sample Output
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 。
Source
题解:最大权闭合子图+tarjan+拓扑序
因为有些位置会受到植物的保护,所以只有僵尸将保护他的所有植物全部吃掉后才能得到这个位置的价值。我们可以从受保护的位置向保护他的位置连边。因为是从右向左发起攻击,所以只有右边的植物被吃掉才能吃到左边的植物,所以我们对于每一行从左向右连边。我们之所以这么建图是因为边的指向表示依赖关系,也就是这个点选了,那么他连出的点一定选了,符合最大权闭合子图的模型,也符合题目中的要求。这样进行转换之后我们发现其实有些位置是无论如何都攻击不到的,那么我们的目标是转化成最大权闭合子图的模型,所以我们进行tarjan缩点,如果一个连通块内有不止一个点的话,那么这个连通块内的点一定都无法得到,同样有边连向的这个连通块的点也无法得到,我们可以tarjan后反向建边,按照拓扑序,进行更新。
最后网络流建图的时候只保留能到达的点和边,然后按照最大权闭合子图的方式求解即可。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#define N 800003
#define inf 1000000000
using namespace std;
int tot,next[N],point[N],v[N],remain[N],ck[N];
int deep[N],num[N],cur[N],last[N];
int tt,head[N],u[N],nxt[N],n,m,s,t,mark[N],mp[N];
int ins[1000],low[1000],dfsn[1000],size[1000],st[N],top,sz,cnt,belong[1000],id[53][53],val[1000];
void add(int x,int y,int z)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
//cout<<x<<" "<<y<<" "<<z<<endl;
}
void build(int x,int y)
{
tt++; nxt[tt]=head[x]; head[x]=tt; u[tt]=y;
//cout<<x<<" "<<y<<endl;
}
int addflow(int s,int t)
{
int now=t; int ans=inf;
while (now!=s) {
ans=min(ans,remain[last[now]]);
now=v[last[now]^1];
}
now=t;
while (now!=s) {
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}
return ans;
}
void bfs(int s,int t)
{
for (int i=s;i<=t;i++) deep[i]=t;
queue<int> p; p.push(t); deep[t]=0;
while (!p.empty()){
int now=p.front(); p.pop();
for (int i=point[now];i!=-1;i=next[i])
if (deep[v[i]]==t&&remain[i^1])
deep[v[i]]=deep[now]+1,p.push(v[i]);
}
}
int isap(int s,int t)
{
int now=s; int ans=0;
bfs(s,t);
for (int i=s;i<=t;i++) num[deep[i]]++;
for (int i=s;i<=t;i++) cur[i]=point[i];
while (deep[s]<t) {
if (now==t) {
ans+=addflow(s,t);
now=s;
}
bool pd=false;
for (int i=cur[now];i!=-1;i=next[i])
if (deep[v[i]]+1==deep[now]&&remain[i]) {
cur[now]=i;
last[v[i]]=i;
now=v[i];
pd=true;
break;
}
if (!pd) {
int minn=t;
for (int i=point[now];i!=-1;i=next[i])
if (remain[i]) minn=min(minn,deep[v[i]]);
if (!--num[deep[now]]) break;
num[deep[now]=minn+1]++;
cur[now]=point[now];
if (now!=s) now=v[last[now]^1];
}
}
return ans;
}
void tarjan(int x)
{
st[++top]=x; ins[x]=1;
dfsn[x]=low[x]=++sz;
for (int i=head[x];i;i=nxt[i]){
int j=u[i];
if (!dfsn[j]) tarjan(j),low[x]=min(low[x],low[j]);
else if (ins[j]) low[x]=min(low[x],dfsn[j]);
}
if (low[x]==dfsn[x]) {
++cnt; int j;
do{
j=st[top--];
belong[j]=cnt;
mp[cnt]=j;
size[cnt]++;
ins[j]=0;
}while (j!=x);
}
}
void rebuild(int x,int y)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; ins[y]++;
//cout<<x<<" "<<y<<endl;
}
void solve()
{
memset(ins,0,sizeof(ins));
for (int i=1;i<=n*m;i++)
for (int j=head[i];j;j=nxt[j])
if (belong[i]!=belong[u[j]])
rebuild(belong[u[j]],belong[i]);
queue<int> p;
for (int i=1;i<=cnt;i++)
if (!ins[i]) p.push(i);
while (!p.empty()) {
int now=p.front(); p.pop();
for (int i=point[now];i;i=next[i]){
ins[v[i]]--;
if (ck[mp[now]]) ck[mp[v[i]]]=1;
if (!ins[v[i]]) p.push(v[i]);
}
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) id[i][j]=++cnt;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) {
scanf("%d",&val[id[i][j]]);
int k; scanf("%d",&k);
for (int l=1;l<=k;l++) {
int x,y; scanf("%d%d",&x,&y);
x++; y++; build(id[x][y],id[i][j]);
}
}
for (int i=1;i<=n;i++)
for (int j=2;j<=m;j++)
build(id[i][j-1],id[i][j]);
cnt=0; s=1; t=n*m+2;
for (int i=1;i<=n*m;i++)
if (!dfsn[i]) tarjan(i);
for (int i=1;i<=n*m;i++)
if (size[belong[i]]!=1) ck[i]=1;
solve();
tot=-1;
memset(point,-1,sizeof(point));
for (int i=1;i<=n*m;i++)
for (int j=head[i];j;j=nxt[j])
if (!ck[i]&&!ck[u[j]]) add(i+1,u[j]+1,inf);
int sum=0;
//for (int i=1;i<=n*m;i++) cout<<ck[i]<<" ";
//cout<<endl;
for (int i=1;i<=n*m;i++)
if (!ck[i]){
if (val[i]>0) add(s,i+1,val[i]),sum+=val[i];
else add(i+1,t,-val[i]);
}
int flow=isap(s,t);
printf("%d\n",sum-flow);
}