给定一个 n×n 的棋盘,棋盘上每个位置要么为空要么为障碍。定义棋盘上两个位置 (x,y) 与 (u,v) 能互相攻击当且仅当满足以下两个条件:
∙ x=u 或 y=v
∙ (x,y) 与 (u,v) 之间的所有位置,均不是障碍。
有 q 个询问,每个询问给定 ki ,要求从棋盘中选出 ki 个空位置来放棋子,问最少互相能攻击到的棋子对数是多少?
n≤50,q≤104,k≤ 棋盘中空位置数量
考虑将所有的行连通块和列连通块提出来,一个空位会同时属于一个行连通块和一个列连通块。
两个空位显然最多只会同属一个连通块。一个连通块如果有 x 个点,那么其会造成 (x−1)x2 的代价。
考虑使用网络流,将所有的行连通块和列连通块都建点,分居两侧,原点向每一个行连通块连 n 条边,流量上限都是 1 ,第 i 条边的费用是 i−1 ,列连通块向汇点连边类似。对于一个空位,我们在其两个连通块之间连一条流量上限为正无穷,费用为 0 的边。然后直接跑最小费用流最大流就好了。为了处理询问,我们直接每次只流 1 的流量然后计入答案就行了。如果你想跑得更快可以动态加边。
时间复杂度 O(Maxflow(n2,n3)) 。
#include <algorithm>
#include <iostream>
#include <climits>
#include <cstdio>
#include <cctype>
#include <queue>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
char getnxt()
{
char ch=getchar();
while (ch!='.'&&ch!='#') ch=getchar();
return ch;
}
int buf[30];
void write(int x)
{
if (x<0) putchar('-'),x=-x;
for (;x;x/=10) buf[++buf[0]]=x%10;
if (!buf[0]) buf[++buf[0]]=0;
for (;buf[0];putchar('0'+buf[buf[0]--]));
}
const int N=55;
const int K=N*N;
const int V=N*N*3;
const int E=(N*N*2+N*N*N*2)<<1;
const int INF=INT_MAX/2;
int rid[N][N],cid[N][N];
bool pic[N][N];
int ans[K];
int n,q,cnt1,cnt2,pts;
struct network
{
int nxt[E],tov[E],r[E],f[E],v[E],c[E];
int last[V],dis[V],fr[V],ed[V];
bool exist[V];
queue<int> q;
int S,T,mincost,tot;
void insert(int x,int y,int full,int cost,int rev){tov[++tot]=y,nxt[tot]=last[x],f[tot]=full,c[tot]=cost,r[tot]=tot+rev,last[x]=tot;}
void addedge(int x,int y,int full,int cost){insert(x,y,full,cost,1),insert(y,x,0,-cost,-1);}
void build()
{
S=0,T=pts+cnt1+cnt2+1;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
addedge(rid[i][j]+pts,cid[i][j]+pts+cnt1,1,0);
for (int i=1;i<=cnt1;++i)
for (int j=0;j<n;++j)
addedge(S,i+pts,1,j);
for (int i=1;i<=cnt2;++i)
for (int j=0;j<n;++j)
addedge(i+pts+cnt1,T,1,j);
}
void aug()
{
for (int x=S;x<=T;++x) dis[x]=INF,fr[x]=-1;
dis[S]=0,q.push(S),exist[S]=1;
for (int x,i,y;!q.empty();)
for (i=last[x=q.front()],q.pop(),exist[x]=0;i;i=nxt[i])
if (f[i]-v[i]&&dis[y=tov[i]]>dis[x]+c[i])
{
dis[y]=dis[x]+c[i],fr[y]=x,ed[y]=i;
if (!exist[y]) q.push(y),exist[y]=1;
}
if (dis[T]==INF) return;
for (int x=T,i;x!=S;x=fr[x]) i=ed[x],++v[i],--v[r[i]];
mincost+=dis[T];
}
}net;
void pre()
{
cnt1=0;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
if (pic[i][j]&&!rid[i][j])
{
++cnt1;
for (int x=i,y=j;y<=n&&pic[x][y];++y) rid[x][y]=cnt1;
}
for (int j=1;j<=n;++j)
for (int i=1;i<=n;++i)
if (pic[i][j]&&!cid[i][j])
{
++cnt2;
for (int x=i,y=j;x<=n&&pic[x][y];++x) cid[x][y]=cnt2;
}
net.build();
for (int i=1;i<=pts;++i) net.aug(),ans[i]=net.mincost;
}
int main()
{
freopen("chess.in","r",stdin),freopen("chess.out","w",stdout);
n=read(),pts=0;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
pts+=pic[i][j]=getnxt()=='.';
pre();
for (q=read();q--;write(ans[read()]),putchar('\n'));
fclose(stdin),fclose(stdout);
return 0;
}