LOJ6068-费用流

LOJ6068

题目描述

题目描述

题解

对于棋盘的题目,其实非常明显的套路就是行与列连边,如果这条边有流量,那么说明这个位置放了棋子。
对于该题,大致思想类似,只不过我们将行与列缩成连通块,然后再进行连边,最后跑最小费用流即可

代码

#include<bits/stdc++.h>
#define M 500009
using namespace std;
int read(){
	int f=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){f=-1,ch=getchar();}
	for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
const int inf=1e9+7;
char s[52][52];
int vis[M],d[M],w[M],first[M],nxt[M],to[M],val[M],tot=1,maxn,cnt,fr[M];
int l[51][51],r[51][51],numx[M],numy[M],num1,num2,ans[M],n,S,T,pre[M],m;
void add(int x,int y,int z,int v){
	nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z,val[tot]=v,fr[tot]=x;
	nxt[++tot]=first[y],first[y]=tot,to[tot]=x,w[tot]=0,val[tot]=-v,fr[tot]=y;
} 
void bfs(){
	for(int i=1;i<=T;i++) vis[i]=0,d[i]=inf;
	queue<int>q;d[S]=0;
	q.push(S),vis[S]=1;
	while(q.size()){
		int u=q.front();
		q.pop();vis[u]=0;
		for(int i=first[u];i;i=nxt[i]){
			int v=to[i];
			if(w[i]&&d[v]>d[u]+val[i]){
				d[v]=d[u]+val[i],pre[v]=i;
				if(!vis[v]) q.push(v),vis[v]=1;
			}
		}
	}
	//for(int i=1;i<=T;i++) printf("%d ",pre[i]);
	if(d[T]==inf) return;
	int minx=inf;
	for (int i=T;i!=S;i=fr[pre[i]]) minx=min(w[pre[i]], minx);
    for (int i=T;i!=S;i=fr[pre[i]]){
        w[pre[i]]-=minx;
        w[pre[i]^1]+=minx;
        maxn+=val[pre[i]]*minx;
    }
}
int main(){
	n=read();
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			if(s[i][j]=='#'){cnt++;continue;}
			if(s[i][j-1]=='.') r[i][j]=r[i][j-1];
			else r[i][j]=++num1;
			if(s[i-1][j]=='.') l[i][j]=l[i-1][j];
			else l[i][j]=++num2;
			numy[l[i][j]]++;numx[r[i][j]]++;
		}S=num1+num2+1,T=S+1;
	for(int i=1;i<=num1;i++)
		for(int j=0;j<numx[i];j++) add(S,i,1,j);
	for(int i=1;i<=num2;i++)
		for(int j=0;j<numy[i];j++) add(i+num1,T,1,j);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(s[i][j]=='.') add(r[i][j],num1+l[i][j],1,0);
	for(int i=1;i<=n*n-cnt;i++) bfs(),ans[i]=maxn;
	m=read();
	for(int i=1;i<=m;i++){
		int x=read();
		if(x>n*n-cnt) printf("0\n");
		else printf("%d\n",ans[x]);
	}return 0;
} 

启发

1,棋盘题目的套路:行与列的连边
2,本题网络流的建图方式值得学习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值