【ACL Contest 1C】Moving Pieces(最小费用最大流)

题目链接

对每个piece一遍BFS求出到每个格子的距离。然后问题就相当于为每个piece选一个格子使得距离之和最大,且格子不能重复选。

可以想到费用流模型,每个piece和每个格子一个点,对于piece x和格子y,若x可移动到y,设x能到的最远距离为d,而x到y的距离为dis,那么就连边x->y,流量1,费用d - dis。最后连s和所有piece,所有格子和t,均为流量1费用0,跑最小费用最大流。

答案就是所有 d 的和减去最小费用。

const int N=105;
int n,m,a,res;
char S[N][N];
queue<int>qx,qy;
int d[N][N];
const int dx[ ]={1,0};
const int dy[ ]={0,1};
vector<int>E[N];
struct ex{int num,next,w,c;}map[100*50*50*2+1005];
int head[100+50*50+105],len=1;
inline void link(int x,int y,int w,int c)
{
  len++;map[len].num=y;map[len].next=head[x];map[len].w=w;map[len].c=c;head[x]=len;
  len++;map[len].num=x;map[len].next=head[y];map[len].w=0;map[len].c=-c;head[y]=len;
}
void BFS(int x,int y)
{
  int i,j,xx,yy,mx=1;
  qx.push(x);qy.push(y);
  for(i=1;i<=n;i++)
    for(j=1;j<=m;j++)
      d[i][j]=0;
  d[x][y]=1;
  while(!qx.empty( ))
    {
      x=qx.front( );qx.pop( );
      y=qy.front( );qy.pop( );
      for(i=0;i<=1;i++)
		{
		  xx=x+dx[i];
		  yy=y+dy[i];
		  if(xx>n||yy>m||S[xx][yy]=='#'||d[xx][yy]) continue;
		  d[xx][yy]=d[x][y]+1;
		  mx=max(mx,d[xx][yy]);
		  qx.push(xx);
		  qy.push(yy);
		}
    }
  res+=mx;
  for(i=1;i<=n;i++)
    for(j=1;j<=m;j++)
      if(d[i][j]) link(a,100+(i-1)*m+j,1,mx-d[i][j]+1);
}
const int DN = 100+50*50+105;
int s,t,ans,dis[DN],pr[DN];
bool in[DN];
queue<int>q;
const int orz=1e9;
bool SPFA( )
{
  int i,x,y,c,w;
  for(i=1;i<=t;i++) dis[i]=orz;
  q.push(s);
  while(!q.empty( ))
    {
      x=q.front( );q.pop( );in[x]=0;
      for(i=head[x];i;i=map[i].next)
		if(map[i].w)
		  {
		    y=map[i].num;c=map[i].c;
		    if(dis[y]>dis[x]+c) 
		      {
				dis[y]=dis[x]+c;pr[y]=i;
				if(!in[y]) in[y]=1,q.push(y);
		      }
		  }
    }
  if(dis[t]==orz) return 0;
  for(w=orz,i=t;i!=s;i=map[x^1].num) x=pr[i],w=min(w,map[x].w);
  for(i=t;i!=s;i=map[x^1].num) x=pr[i],map[x].w-=w,map[x^1].w+=w;
  ans+=dis[t]*w;
  return 1;
}
int main( )
{
  int i,j;
  read(n);read(m);
  for(i=1;i<=n;i++) scanf("%s",S[i]+1);
  for(i=1;i<=n;i++)
    for(j=1;j<=m;j++)
      {
		if(S[i][j]=='o')
		  {
		    a++;
		    BFS(i,j);
		  }
      }
  s=0;
  t=100+n*m+1;
  for(i=1;i<=a;i++) link(s,i,1,0);
  for(i=1;i<=n;i++)
    for(j=1;j<=m;j++)
      link(100+(i-1)*m+j,t,1,0);
  while(SPFA( ));
  printf("%d",res-ans);
  return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值