Description
-
放假了无聊的小 W 和小 S 在玩游戏。游戏规则是这样的:
-
现在有个 N × M 的空棋盘,要往格子里摆棋子。每个格子可以摆一个棋子,但是每行每列都有各自的棋子数目的上限和下限。
-
小 S 提出了个他喜欢的摆放方案,不一定合法,但不会超过上限限制。
-
小 W 需要找到一个合法的方案,使得跟小 S 的方案状态不同的格子数目最少。一个格子如果在一个方案中摆了棋子,但在另一个方案中没摆棋子,则视为状态不同。两个方案摆放的棋子数目可以不相同。
-
n , m < = 50 n,m<=50 n,m<=50
Solution
- 非常裸的上下界费用流的问题,注意要优化连边,并不需要每一个格子开一个点,只需要每一行、每一列一个点就好。
- 但是这题有负权边,会出现负环。
负权图网络流
- 一种简便的方法是先钦定所有的负权边满流(加入它们的反向边)。那么原图中就只有正权边了。然后需要平衡流量。
- 建立超级源ss、超级汇tt,类似上下界网络流一样平衡——ss->y,x->tt。
- 然后跑在新图上最小费用最大流即可消除负环,相当于是平衡流量,从超级源连出去的流量来代替负权边的对应的入度,连向超级汇对应负权边的出度。
- 在残量网络上再跑一跑。
关于这题
- 简单粗暴的方法是在原来上下界的超级源和超级汇外再开一个超超级源和超超级汇。消掉负环后再回到原来的图上跑。
- 但是因为这题的负权边与ss和tt无关,可以把ss当做sss,tt当做ttt。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 105
#define maxm 200005
using namespace std;
int n,m,kk,i,j,k,ss,tt,s,t,tot,a[maxn][maxn];
int em,e[maxm],nx[maxm],ls[maxn],cost[maxm],ec[maxm];
int ans;
void link(int x,int y,int v,int c){
if (c<0){
link(y,x,v,-c);
link(ss,y,v,0);
link(x,tt,v,0);
ans+=c*v;
} else {
em++,e[em]=y; nx[em]=ls[x]; ls[x]=em; ec[em]=v; cost[em]=c;
em++,e[em]=x; nx[em]=ls[y]; ls[y]=em; ec[em]=0; cost[em]=-c;
}
}
void insert(int x,int y,int L,int R,int c){
if (L) link(ss,y,L,c),link(x,tt,L,0);
if (R-L) link(x,y,R-L,c);
}
void build(){
em=1,s=++tot,t=++tot,ss=++tot,tt=++tot;
for(i=1;i<=n;i++){
scanf("%d%d",&j,&k);
insert(s,i,j,k,0);
}
for(i=1;i<=m;i++){
scanf("%d%d",&j,&k);
insert(n+i,t,j,k,0);
}
for(i=1;i<=kk;i++) scanf("%d%d",&j,&k),a[j][k]=1;
for(i=1;i<=n;i++) for(j=1;j<=m;j++)
insert(i,n+j,0,1,(a[i][j])?-1:1);
insert(t,s,0,2e9,0);
}
int h,w,d[maxm],dis[maxn],fai[maxn],fa[maxn],vis[maxn];
int SPFA(){
memset(dis,127,sizeof(dis));
memset(vis,0,sizeof(vis));
h=0,w=1,d[1]=ss,dis[ss]=0,vis[ss]=1;
while (h<w){
int x=d[++h];
for(i=ls[x];i;i=nx[i]) if (ec[i]&&dis[x]+cost[i]<dis[e[i]]){
dis[e[i]]=dis[x]+cost[i];
fa[e[i]]=x,fai[e[i]]=i;
if (!vis[e[i]])
d[++w]=e[i],vis[e[i]]=1;
}
vis[x]=0;
}
return dis[tt]<2e9;
}
void maxflow(){
while (SPFA()){
ans+=dis[tt];
for(int x=tt;x!=ss;x=fa[x])
ec[fai[x]]--,ec[fai[x]^1]++;
}
}
int main(){
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
// freopen("ceshi.in","r",stdin);
// freopen("ceshi.out","w",stdout);
scanf("%d%d%d",&n,&m,&kk),tot=n+m,ans=kk;
build();
maxflow();
printf("%d",ans);
}