题解:
是一个最小费用最大流,建立源点汇点,x向可以互相攻击到的为一个集体,所有的集体作为一纵,连源点;同样,y向可以互相攻击到的为一个集体,所有的集体为一纵,与汇点连边;不难得出,x[i]y[j]对应唯一一个点,这样的x与y连边流量1,费用0。每次找流,就在这个集团x与S再连一条1,++h[v];T与集团y也连一条
下面图示
代码:
这个代码是非计划连边,按理说一个集团有多少个点才连多少边,但是非计划连边由于流量限制应该也是对的,还是写了一个计划连边的。
#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
#define INF 1e9+7
#define N 25000+5
using namespace std;
bool vis[N];
int tot=-1,x[55][55],y[55][55],d[2505],s,t,hv[2505],ans[N];
char a[55][55];
int nxt[N*2+5],point[N*2+5],remind[N*2+5],v[N*2+5],last[N*2+5],dis[N*2+5],co[N*2+5];
void addline(int x,int y,int cap,int cost)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remind[tot]=cap; co[tot]=cost;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remind[tot]=0; co[tot]=-cost;
}
bool spfa()
{
queue<int>q;
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
vis[s]=true;
q.push(s);
while (!q.empty())
{
int now=q.front(); q.pop();
vis[now]=false;
for (int i=point[now];i!=-1;i=nxt[i])
if (dis[v[i]]>dis[now]+co[i] && remind[i])
{
dis[v[i]]=dis[now]+co[i];
last[v[i]]=i;
if (!vis[v[i]])
{
vis[v[i]]=true; q.push(v[i]);
}
}
}
return dis[t]<INF;
}
int main()
{
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
int n,i,j,q,cnt=0;
scanf("%d",&n);
memset(point,-1,sizeof(point));
memset(nxt,-1,sizeof(nxt));
for(int i=1;i<=n;i++)
scanf("%s",a[i]+1);
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
{
++cnt;
for (;j<=n && a[i][j]=='.';j++) x[i][j]=cnt;
}
int tt=cnt;
for (j=1;j<=n;j++)
for (i=1;i<=n;i++)
{
++cnt;
for (;i<=n && a[i][j]=='.';i++) y[i][j]=cnt;
}
int maxx=0,l;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (a[i][j]=='.')
addline(x[i][j],y[i][j],1,0);
for (i=1;i<=tt;i++)addline(0,i,1,0);
for (i=tt+1;i<=cnt;i++)addline(i,cnt+1,1,0);
ans[0]=0; int num=0;
s=0,t=cnt+1;int k,la;
while (spfa())//如果还有流
{
++num;
ans[num]=ans[num-1]+dis[t];//最大流量肯定是1
int now=t;
while (now)
{
remind[last[now]]--;
remind[last[now]^1]++;
la=now;
now=v[last[now]^1];
}
addline(0,la,1,++hv[la]);
addline(v[last[t]^1],t,1,++hv[v[last[t]^1]]);
}
scanf("%d",&q);
for (i=1;i<=q;i++)
{
scanf("%d",&k);
printf("%d\n",ans[k]);
}
}
以下是计划连边,速度慢于非计划。
#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
#define INF 1e9+7
#define N 25000+5
using namespace std;
bool vis[N];
int tot=-1,x[55][55],y[55][55],d[2505],s,t,hv[2505],ans[N];
char a[55][55];
int nxt[N*2+5],point[N*2+5],remind[N*2+5],v[N*2+5],last[N*2+5],dis[N*2+5],co[N*2+5];
void addline(int x,int y,int cap,int cost)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remind[tot]=cap; co[tot]=cost;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remind[tot]=0; co[tot]=-cost;
}
bool spfa()
{
queue<int>q;
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0;
vis[s]=true;
q.push(s);
while (!q.empty())
{
int now=q.front(); q.pop();
vis[now]=false;
for (int i=point[now];i!=-1;i=nxt[i])
if (dis[v[i]]>dis[now]+co[i] && remind[i])
{
dis[v[i]]=dis[now]+co[i];
last[v[i]]=i;
if (!vis[v[i]])
{
vis[v[i]]=true; q.push(v[i]);
}
}
}
return dis[t]<INF;
}
int main()
{
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
int n,i,j,q,cnt=0;
scanf("%d",&n);
memset(point,-1,sizeof(point));
memset(nxt,-1,sizeof(nxt));
for(int i=1;i<=n;i++)
scanf("%s",a[i]+1);
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
{
++cnt;
for (;j<=n && a[i][j]=='.';j++)
x[i][j]=cnt,addline(0,cnt,1,hv[cnt]++);
}
int tt=cnt;
for (j=1;j<=n;j++)
for (i=1;i<=n;i++)
{
++cnt;
for (;i<=n && a[i][j]=='.';i++) y[i][j]=cnt;
}
int maxx=0,l;s=0,t=cnt+1;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (a[i][j]=='.')
addline(x[i][j],y[i][j],1,0);
for (j=1;j<=n;j++)
for (i=1;i<=n;i++)
{
++tt;
for (;i<=n && a[i][j]=='.';i++) addline(tt,t,1,hv[tt]++);
}
ans[0]=0; int num=0;
int k,la;
while (spfa())//如果还有流
{
++num;
ans[num]=ans[num-1]+dis[t];//最大流量肯定是1
int now=t;
while (now)
{
remind[last[now]]--;
remind[last[now]^1]++;
now=v[last[now]^1];
}
}
scanf("%d",&q);
for (i=1;i<=q;i++)
{
scanf("%d",&k);
printf("%d\n",ans[k]);
}
}