题目
有一个N*M的方格,每个格子有一个权值,每个格子可以保护制定格子以及它后面的格子,只有当一个格子没有被保护的格子的时候才能被获取,求最大权值(原权值有正负)。
分析
最大权闭合子图的裸题。
然而最大权闭合子图是啥?
概念
闭合子图:选取一些结点使得这些结点所有出边都不会指向当前结点们之外的边。
最大权闭合子图:选取权值和最大的
它的应用就比如说大学选课,需要先修课,所有结点向自己的先修课连边,这样选取的最大权闭合子图就是最优值。
注意:最大权闭合子图可以不是一个连通块。
求法
建图
- 首先建立原来的那个DAG图,如果不是DAG需要先拓扑排序去掉环(不能用tarjan)
- 对于所有的边(u,v)全部在网络流中连边(u,v),流量为inf(inf至少为所有权值的和)
- 对于所有正权结点,连接(s,u),权值为w;对于所有负权结点,连接(u,t),权值为-w(-w是个正数)
答案
ans=正权和-最小割(因为证明是用最小割证的)
和S连通的就是选择了的结点。
继续分析
那么就是个裸题了
注意由于两个点之间会被多次连边,所以最好用邻接矩阵
之前说过不能用tarjan,不过用了也只会错两个点。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=32*42,maxm=maxn*maxn,inf=1e9;
namespace IStream{
const int L=1<<15;
char buffer[L],*S,*T;
inline char Get_Char()
{
if(S==T)
{
T=(S=buffer)+fread(buffer,1,L,stdin);
if(S==T) return EOF;
}
return *S++;
}
inline void Rd(int &re)
{
char c;
re=0;
int k=1;
for(c=Get_Char();(c<'0'||c>'9') && c!='-';c=Get_Char());
if(c=='-')c=Get_Char(),k=-1;
while(c>='0'&&c<='9')
re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
re*=k;
}
}
namespace maxflow
{
struct edge
{
int to,next,cap,flow;
}E[maxm<<1];
int sum;
int np,first[maxn];
void add(int u,int v,int c)
{
E[++np]=(edge){v,first[u],c,0};
first[u]=np;
E[++np]=(edge){u,first[v],0,0};
first[v]=np;
}
int n,m,s,t;
int dist[maxn],gap[maxn];
void Initial()
{
np=-1;
memset(first,-1,sizeof(first));
}
int SAP(int i,int lim)
{
if(i==t)return lim;
int flow=0;
for(int p=first[i];p!=-1;p=E[p].next)if(E[p].cap-E[p].flow)
{
int j=E[p].to;
if(dist[j]+1==dist[i])
{
int tmp=SAP(j,min(lim-flow,E[p].cap-E[p].flow));
flow+=tmp;
E[p].flow+=tmp;
E[p^1].flow-=tmp;
if(flow==lim || dist[s]>=t)return flow;
}
}
if(flow==0)
{
if(--gap[dist[i]]==0)dist[s]=t;
gap[++dist[i]]++;
}
return flow;
}
void maxflow()
{
memset(gap,0,sizeof(gap));
memset(dist,0,sizeof(dist));
gap[0]=t;
int flow=0;
while(dist[s]<t)
flow+=SAP(s,inf);
printf("%d\n",sum-flow);
}
}
namespace topo
{
int np,first[maxn];
bool g[maxn][maxn];
int n,m,nm,w[maxn],rd[maxn];
bool vis[maxn];
void topo()
{
queue<int>q;
for(int i=1;i<=nm;i++)if(!rd[i])q.push(i),vis[i]=1;
while(!q.empty())
{
int i=q.front();q.pop();
for(int j=1;j<=nm;j++)
{
if(!g[i][j])continue;
rd[j]--;
if(!rd[j])q.push(j),vis[j]=1;
}
}
}
#define getId(i,j) (i-1)*m+j
void Init()
{
int id,id1,a,ni,nj;
IStream::Rd(n);IStream::Rd(m);
nm=n*m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
id=id1=getId(i,j);
IStream::Rd(w[id]);
for(int k=j+1;k<=m;k++)
{
id1++;
if(!g[id1][id])g[id1][id]=1,rd[id]++;
}
IStream::Rd(a);
for(int k=1;k<=a;k++)
{
IStream::Rd(ni);IStream::Rd(nj);
ni++,nj++;
for(int l=nj;l>=1;l--)
if(!g[id][getId(ni,l)])g[id][getId(ni,l)]=1,rd[getId(ni,l)]++;
}
}
}
}
void Build()
{
maxflow::Initial();
maxflow::s=nm+1;
maxflow::t=maxflow::s+1;
for(int i=1;i<=nm;i++)
{
for(int j=1;j<=nm;j++)
{
if(!g[i][j])continue;
if(!vis[i] || !vis[j])continue;
maxflow::add(j,i,inf);
}
}
for(int i=1;i<=nm;i++)
{
if(!vis[i])continue;
if(w[i]>0)maxflow::add(maxflow::s,i,w[i]),maxflow::sum+=w[i];
if(w[i]<0)maxflow::add(i,maxflow::t,-w[i]);
}
maxflow::maxflow();
}
void work()
{
Init();
topo();
Build();
}
}
int main()
{
//freopen("in.txt","r",stdin);
topo::work();
return 0;
}