BZOJ 2668: [cqoi2012]交换棋子(费用流)

题目

有一个棋盘,有一些棋子,要求把这些棋子通过和周围的八连块交换,每个格子有交换的次数限制,求从初始状态移动到目标状态的最小交换次数,无解输出-1。

分析

有几个坑点:
- 是八连块
- 交换的时候起点格子和终点格子都只交换一次,而其它格子交换两次

我们把点拆成三个点,其中对于一般的路径用最后一个点连接第一个点。
源点连中间那个店,汇点也连接中间那个点。
点与点之间边连接成inf的流量0的费用,点内部连接费用为1
然后根据贪心看当前结点有没有点连入或者连出,选择尽量可以多流的方式来为点内的三个分点连边。
最后由于每次交换我们都算了两个费用,所以答案要/2

代码

#include<queue>
#include<cctype>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1500,maxm=maxn*8,inf=1e9;
int np,first[maxn];
struct edge{
    int from,to,next,cap,flow,cost;
}E[maxm<<1];
void add(int u,int v,int w,int c)
{
    //if(w)cout<<u<<" "<<v<<" "<<w<<" "<<c<<endl;
    E[++np]=(edge){u,v,first[u],w,0,c};
    first[u]=np;
}
int n,m,s,t,nm,cnt1,cnt2;
int aa[maxn][maxn],b[maxn][maxn],c[maxn][maxn];
int getId1(int x,int y)
{
    return (x-1)*m+y;
}
int getId2(int x,int y)
{
    return (x-1)*m+y+nm;
}
int getId3(int x,int y)
{
    return (x-1)*m+y+2*nm;
}
int dx[]={1,1,1,-1,-1,-1,0,0};
int dy[]={1,0,-1,1,0,-1,1,-1};
void Init()
{
    int id1,id2,id3;
    np=-1;
    memset(first,-1,sizeof(first));
    scanf("%d%d",&n,&m);
    nm=n*m;
    s=nm*3+1,t=s+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%1d",&aa[i][j]);
            if(aa[i][j])
            {
                cnt1++;
                id2=getId2(i,j);
                add(s,id2,1,0);
                add(id2,s,0,0);
            }
        }

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%1d",&b[i][j]);
            if(b[i][j])
            {
                cnt2++;
                id2=getId2(i,j);
                add(id2,t,1,0);
                add(t,id2,0,0);
            }
        }

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%1d",&c[i][j]);
            if(c[i][j])
            {
                id1=getId1(i,j);id2=getId2(i,j);id3=getId3(i,j);
                if(~c[i][j]&1)
                {
                    add(id1,id2,c[i][j]/2,1);
                    add(id2,id1,0,-1);
                    add(id2,id3,c[i][j]/2,1);
                    add(id3,id2,0,-1);
                }
                else
                {
                    if(aa[i][j])
                    {
                        add(id1,id2,c[i][j]/2,1);
                        add(id2,id1,0,-1);
                        add(id2,id3,c[i][j]/2+1,1);
                        add(id3,id2,0,-1);
                    }
                    else
                    {
                        add(id1,id2,c[i][j]/2+1,1);
                        add(id2,id1,0,-1);
                        add(id2,id3,c[i][j]/2,1);
                        add(id3,id2,0,-1);
                    }
                }
            }
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int ni,nj;
            id3=getId3(i,j);
            for(int k=0;k<8;k++)
            {
                ni=i+dx[k],nj=j+dy[k];
                if(ni<1 || nj<1 || ni>n || nj>m)continue;
                id1=getId1(ni,nj);
                add(id3,id1,9,0);
                add(id1,id3,0,0);
            }
        }
}
int a[maxn],dist[maxn],fa[maxn],inq[maxn];
queue<int>q;
bool SPFA(int s,int t)
{

    while(!q.empty())q.pop();
    memset(a,0,sizeof(a));a[s]=inf;
    memset(inq,0,sizeof(inq));q.push(s);
    for(int i=1;i<=t;i++)dist[i]=inf;dist[s]=0;
    fa[s]=-1;
    while(!q.empty())
    {
        int i=q.front();q.pop();
        inq[i]=0;
        for(int p=first[i];p!=-1;p=E[p].next)
        {
            int j=E[p].to;
            if(E[p].cap-E[p].flow)
            if(dist[i]+E[p].cost<dist[j])
            {
                a[j]=min(a[i],E[p].cap-E[p].flow);
                dist[j]=dist[i]+E[p].cost;
                fa[j]=p;
                if(!inq[j])inq[j]=1,q.push(j);
            }
        }
    }
    return a[t];
}
void mincost()
{
    int flow=0,cost=0;
    while(SPFA(s,t))
    {
        flow+=a[t];
        cost+=dist[t]*a[t];
        for(int p=fa[t];p!=-1;p=fa[E[p].from])
        {
            E[p].flow+=a[t];
            E[p^1].flow-=a[t];
        }
    }
    if(flow!=max(cnt1,cnt2))cost=-2;
    printf("%d",cost/2);
}
int main()
{
    //freopen("in.txt","r",stdin);
    Init();
    mincost();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值