题目描述
输入
输出
仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。
样例输入
3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0
样例输出
25
。。一看就是一个网络流,首先有两种植物,一种有收益,一种需花费。
我们可以尝试着想到最小割,先统计出总收益tot(所有收益之和),在想办法构造出一个图,
使得它的每一个割对应:不要的收益+所花的花费 。那么这时,答案就等于 tot-最小割。
至于怎么构图,我们可以吧最小割割开的图看做S->T集(即和源或汇点分在一个集合中的点集)
S集中的点一定是被吃掉的植物,相反,T中就是没有被吃的。
于是,我们考虑将收益和花费的植物分别将S,T连边,有因为僵尸是从左向右吃的,要想吃掉一棵植物必须吃掉保护他的那棵植物,对于这样的拓扑关系,很明显要先将先被吃的那棵植物划分到S集,也就是将他和T的连边断掉,于是对于每一点,我们向在拓扑关系中的他上一层的节点连一条为INF的边。
不过这里要注意,当拓扑关系中存在环时,在流网络中就形成了一个INF的环路,最小割割掉的一定是环路与S,T的所有边,那么这就错了。
于是我们必须预处理拓扑关系图中的所有环路,将所有不可能到达的点删去,再构图跑网络流,这里JeremyGuo提供了一个很方便的解决方案:我们对于原图中的每一个强联通分量都从中找出一个点,将其与T连一条INF边,那么这个强连通分量就一定属于T集合,于是就只算了不需要的收益。至于找点向T连边,这个有很多搞法,而且又不是一个分量只能连一条,所以乱搞搞就行了。
/**************************************************************
Problem: 1565
User: szpszp
Language: C++
Result: Accepted
Time:188 ms
Memory:29440 kb
****************************************************************/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 20
#define MAXM 30
#define INF 0x3f3f3f3f
typedef long long int LL;
template<class T>
void Read(T &x){
x=0;char c=getchar();bool flag=0;
while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
if(flag)x=-x;
}
namespace ISAP{
const int MAXP = MAXN*MAXM;
const int MAXE = MAXN*MAXM*MAXN*MAXM*2;
struct node{
int v,c;
node *nxt,*bck;
}*adj[MAXP+10],Edges[MAXE*2+100],*New;
int d[MAXP+10],vd[MAXP+10];
int S,T,P;
void addedges(int u,int v,int c){
node *p=++New;
p->v=v;
p->c=c;
p->nxt=adj[u];
p->bck=New+1;
adj[u]=p;
p=++New;
p->v=u;
p->c=0;
p->nxt=adj[v];
p->bck=New-1;
adj[v]=p;
}
void init(int s,int t,int p){
S=s,T=t,P=p;
memset(adj,0,sizeof(adj));
New=Edges;
}
int aug(int x,int augco){
if(x==T)return augco;
int Dmin=P-1,augc=augco,delta;
for(node *p=adj[x];p!=NULL;p=p->nxt)
if(p->c){
if(d[x]==d[p->v]+1){
delta=min(augc,p->c);
delta=aug(p->v,delta);
p->c-=delta;
p->bck->c+=delta;
augc-=delta;
if(d[S]>=P)
return augco-augc;
if(!augc)
break;
}
Dmin=min(Dmin,d[p->v]);
}
if(augco==augc){
if(!(--vd[d[x]]))
d[S]=P;
++vd[d[x]=Dmin+1];
}
return augco-augc;
}
int isap(){
memset(d,0,sizeof(d));
memset(vd,0,sizeof(vd));
vd[0]=P;
int flow=0;
while(d[S]<P)
flow+=aug(S,INF);
return flow;
}
}
int w[MAXN+10][MAXM+10];
int n,m;
int id[MAXN+10][MAXM+10],tot;
void makeid(int n,int m){
tot=0;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
id[i][j]=++tot;
}
struct node{
int v;
node *nxt;
}*adj[MAXN*MAXM+10],Edges[MAXN*MAXM*MAXN*MAXM*2+100],*New=Edges;
void addedge(int u,int v){
node *p=++New;
p->v=v;
p->nxt=adj[u];
adj[u]=p;
}
bool vis[MAXN*MAXM+10],tag[MAXN*MAXM+10];
vector<int>point;
void dfs(int x){
vis[x]=tag[x]=1;
for(node *p=adj[x];p!=NULL;p=p->nxt){
if(!vis[p->v])dfs(p->v);
else if(tag[p->v])point.push_back(p->v);
}
tag[x]=0;
}
int main(){
Read(n),Read(m);
makeid(n,m);
int t,x,y;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j){
Read(w[i][j]);
Read(t);
for(int k=0;k<t;++k){
Read(x),Read(y);
addedge(id[i][j],id[x][y]);
}
}
for(int i=0;i<n;++i)
for(int j=1;j<m;++j)addedge(id[i][j],id[i][j-1]);
for(int i=1;i<=tot;++i)
if(!vis[i])dfs(i);
ISAP::init(tot+1,tot+2,tot+2);
for(int i=0;i<n;++i)
for(int j=0;j<m;++j){
if(w[i][j]>=0){
ISAP::addedges(ISAP::S,id[i][j],w[i][j]);
//ISAP::addedges(id[i][j],ISAP::T,0);
}
else{
//ISAP::addedges(ISAP::S,id[i][j],0);
ISAP::addedges(id[i][j],ISAP::T,-w[i][j]);
}
for(node *p=adj[id[i][j]];p!=NULL;p=p->nxt)
ISAP::addedges(p->v,id[i][j],INF);
}
for(int i=0;i<point.size();++i)
ISAP::addedges(point[i],ISAP::T,INF);
int ans=0;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
if(w[i][j]>0)ans+=w[i][j];
printf("%d\n",ans-ISAP::isap());
}