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,本题网络流的建图方式值得学习