版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88563170
题意:
n*m的植物矩阵,每个植物有价值v,吃掉一颗植物之前必须先吃掉与它同一行右边的植物,同时植物间有保护关系(x,y),表示吃掉x之前必须先吃掉y。
题目分析:
其实这个题似乎就是 最大权闭合子图问题:
考虑二元关系模型,x选则y必须选,也就是说x选、y不选的代价为inf
那么ans = 正权点之和 - 不选的正权 - 选的|负权|
可以按如图方式建边:
(vx是正权,vy是负权)
x选(保留与S的正边)且 y不选(保留与T的负边),那么有inf的代价,可以看出两者必须割一个。
(以上为简易过程的口胡)
让我们在来仔细地看一看边是怎么建出来的:
好了那就是裸裸的二元关系建图了…吧?
然后一发不过样例
发现这样只能解决无环的图,如果限制条件形成了环,我们并没有排除掉整个环一起选的情况。
所以需要tarjan缩点,如果形成了点数大于1的强联通分量,就把这个分量选的代价置为inf,不同强连通分量之间的边照常连即可。
PS:一直打矩阵的题都要给(i,j)编个号,结果这道题刷新了我的认知。。可以用vector直接存下它的关系边,for循环直接从1到n*m就行了!!!清爽无比~
Code:
#include<cstdio>
#include<cstring>
#include<cctype>
#include<vector>
#include<algorithm>
#define maxn 605
#define maxm 400005
using namespace std;
inline void read(int &a){//一开始读入优化没有判负数。。
char c;bool f=0;
while(!isdigit(c=getchar())) if(c=='-') f=1;
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
if(f) a=-a;
}
const int inf = 0x3f3f3f3f;
int n,m,S,T,sum;
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],c[maxm],tot=1;
inline void line(int x,int y,int z){
nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=z;
nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0;
}
namespace Maxflow{
int d[maxn],vd[maxn],sz;
int aug(int u,int augco)
{
if(u==T) return augco;
int need=augco,delta;
for(int i=cur[u];i;i=nxt[i]) if(c[i]&&d[u]==d[to[i]]+1){
delta=aug(to[i],min(c[i],need));
c[i]-=delta,c[i^1]+=delta;cur[u]=i;
if(!(need-=delta)||d[S]==sz) return augco-need;
}
cur[u]=fir[u];
if(!(--vd[d[u]])) d[S]=sz;
vd[++d[u]]++;
return augco-need;
}
int SAP(){
memset(d,0,sizeof d);
memset(vd,0,sizeof vd);
int flow=0;sz=T+1;
while(d[S]<sz) flow+=aug(S,inf);
return flow;
}
}
vector<int>e[maxn];
int a[maxn],x,y,z;
int dfn[maxn],low[maxn],tim,scc[maxn],siz[maxn],stk[maxn],top,scnt;
void tarjan(int u){
dfn[u]=low[u]=++tim;
stk[++top]=u;
for(int i=e[u].size()-1,v;i>=0;i--){
if(!dfn[v=e[u][i]]) tarjan(v),low[u]=min(low[u],low[v]);
else if(!scc[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
++scnt;
do scc[stk[top]]=scnt,siz[scnt]++; while(stk[top--]!=u);
}
}
int main()
{
read(n),read(m);
for(int i=1;i<=n*m;i++){
read(a[i]);
if(i%m) e[i].push_back(i+1);
read(z);while(z--) read(x),read(y),e[x*m+y+1].push_back(i);
}
for(int i=1;i<=n*m;i++) if(!dfn[i]) tarjan(i);
S=0,T=scnt+1;
for(int i=1;i<=scnt;i++) if(siz[i]>1) line(i,T,inf);
for(int i=1;i<=n*m;i++){
x=scc[i];
if(siz[x]==1){
if(a[i]>0) sum+=a[i],line(S,x,a[i]);
if(a[i]<0) line(x,T,-a[i]);
}
for(int j=e[i].size()-1;j>=0;j--) if(x!=(y=scc[e[i][j]])) line(x,y,inf);
}
printf("%d",sum-Maxflow::SAP());
}