题目
求放k个棋子在n*n的棋盘上,最少互相攻击的棋子对数。
能够攻击仅当两旗子在一条直线上,且中间无障碍物。
共有q个询问
题解
不难想到这种棋盘上放棋子的题考得是网络流
积累:按行、纵分别按其对应连通块编号,S->行->纵->T
但明显新一行的点互相攻击的话,随点数的增加,贡献是0,1,2,3这样递增的。
于是我们想到给S->行,纵->T连边时 连多条(val=0,1,2,3…,flow=1)的边
最后 行->纵 连(val=0,flow=1)即可
好像忘了一件事,多个询问,用费用流每次增一条路,预处理出来就行
注意预估边条数和点个数
#include<bits/stdc++.h>
using namespace std;
const int N=6e3+10,M=2e5+10,Q=1e3+10,INF=1e9+7;
int S,T,MAX_FLOW;
int n;
int head[N],nex[M],to[M],flow[M],val[M],tot=1;
void build(int u,int v,int f,int w)
{
tot++;nex[tot]=head[u];to[tot]=v;flow[tot]=f;val[tot]= w;head[u]=tot;
tot++;nex[tot]=head[v];to[tot]=u;flow[tot]=0;val[tot]=-w;head[v]=tot;
}
int mp[55][55];
int bl[2][55][55];
void make_build()
{
int re0=1,pre0=0;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=n;i++)
{
if(mp[i][j]){if(pre0!=1)re0++;pre0=1;}
else bl[0][i][j]=re0,pre0=0;
}re0++;pre0=1;
}
int re1=1,pre1=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(mp[i][j]){if(pre1!=1)re1++;pre1=1;}
else bl[1][i][j]=re1,pre1=0;
}re1++;pre1=1;
}//pre用来尽量压点个数,但可能没什么用
S=0,T=re0+re1+1,MAX_FLOW=T;
for(int i=1;i<=re0;i++)for(int k=0;k<re1;k++)build(S,i,1,k);
for(int i=1;i<=re1;i++)for(int k=0;k<re0;k++)build(i+re0,T,1,k);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(mp[i][j])continue;
int fw1=bl[0][i][j],fw2=bl[1][i][j]+re0;
build(fw1,fw2,1,0);
}
}
int dis[N],flw[N],pre[N];
bool vis[N];
bool spfa()
{
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
memset(flw,0x7f/3,sizeof(flw));
memset(dis,0x7f/3,sizeof(dis));
queue<int>q;
dis[S]=0;vis[S]=1;q.push(S);
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(dis[v]>dis[u]+val[i]&&flow[i]>0)
{
dis[v]=dis[u]+val[i];
flw[v]=min(flw[u],flow[i]);
pre[v]=i;
if(!vis[v])vis[v]=1,q.push(v);
}
}
}
return dis[T]<0x3F3F3F3F;
}
int doit()
{
int tmp=0;
if(spfa())
{
int f=flw[T];
for(int i=T;i!=S;i=to[pre[i]^1])
{
flow[pre[i]]-=f;
flow[pre[i]^1]+=f;
tmp+=val[pre[i]]*f;
}
}
return tmp;
}
int q;
int ans[2510];
struct qus{
int x,id;
bool operator<(const qus a)const{return x<a.x;}
}qu[Q];
char s[55];
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
int all=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;s[j];j++)
if(s[j]=='#')mp[i][j]=1;
else mp[i][j]=0,all++;
}
make_build();
for(int i=1;i<=all;i++)
ans[i]=ans[i-1]+doit();
scanf("%d",&q);
for(int fw,i=1;i<=q;i++)
{
scanf("%d",&fw);
printf("%d\n",ans[fw]);
}
return 0;
}