码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ2668】【cqoi2012】交换棋子 费用流

时间:2015-03-28 17:19:51      阅读:141      评论:0      收藏:0      [点我收藏+]

标签:bzoj2668   cqoi2012   交换棋子   费用流   

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44702813");
}

前言:

本来以为这种双限制流量的方法很通用很好用,所以没有去写那个一个点拆成俩的奇葩做法……但是后来我发现,这种一个点拆成三个的方法没有任何意义,它只是针对了这道题的特殊性质噗。好像并不能拓展。

题解:

首先图转化成源点往开始图的黑点(当然你要用白点也不是不行)流流量,最终从结束图的黑点流向汇点。这个应该都能想到。

然后关键是怎么在流过一次后同时限制两个点。
这也是我所想知道的……可是,下面的方法并没有告诉我该如何实现它,它用的是分析改流量。[请允许我做一个捂脸熊的表情]

我们分析:
如果一个点是初始图上的黑点,那么它可以

最多可以流入?2?,最多可以流出?+12?

如果一个点是目标图上的黑点,那么它可以

最多可以流入?+12?,最多可以流出?2?

否则

最多可以流入?2?,最多可以流出?2?

然后我们需要特判一下如果一个点既是初始图上的黑点,又是目标图上上的黑点,那么显然它的流入流出都应该跟【否则】那一部分一样。因为流入流出以后跟正常的点一样了么。

然后费用什么乱搞就行了,想设哪就设哪(但是乱设的话最终答案可能需要/2)

注意是八连通。

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1300
#define M 10000
#define P 25
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
using namespace std;
const int dx[]={-1,0,1,-1,1,-1,0,1};
const int dy[]={-1,-1,-1,0,0,1,1,1};
struct KSD
{
    int u,v,len,fee,next;
}e[M];
int head[N],cnt;
inline void add(int u,int v,int len,int fee)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].len=len;
    e[cnt].fee=fee;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int dist[N],s,t;
int lim[N],pre[N];
bool in[N];
queue<int>q;
void spfa()
{
    while(!q.empty())q.pop();
    memset(dist,0x3f,sizeof dist);

    q.push(s),dist[s]=0,lim[s]=inf;
    int i,u,v;
    while(!q.empty())
    {
        u=q.front(),q.pop(),in[u]=0;
        for(i=head[u];i;i=e[i].next)if(e[i].len)
        {
            if(dist[v=e[i].v]>dist[u]+e[i].fee)
            {
                dist[v]=dist[u]+e[i].fee;
                lim[v]=min(e[i].len,lim[u]);
                pre[v]=i;
                if(!in[v])q.push(v),in[v]=1;
            }
        }
    }
    return ;
}
void handle(int flow)
{
    for(int i=pre[t];i;i=pre[e[i].u])
    {
        e[i].len-=flow;
        e[i^1].len+=flow;
    }
}
int minfee,maxflow,n,m,p;
char sta[P][P],end[P][P],str[P];
int id[P][P];
bool build()
{
    int i,j,k;
    int x,y;
    scanf("%d%d",&n,&m);
    s=0,t=n*m*3+1;
    for(i=1;i<=n;i++)for(j=1;j<=m;j++)
        id[i][j]=++cnt,cnt++,cnt++;
    cnt=1;
    for(i=1;i<=n;i++)
    {
        scanf("%s",sta[i]+1);
        for(j=1;j<=m;j++)if(sta[i][j]==‘1‘)
        {
            p++;
            add(s,id[i][j]+1,1,0);
            add(id[i][j]+1,s,0,0);
        }
    }
    maxflow=p;
    for(i=1;i<=n;i++)
    {
        scanf("%s",end[i]+1);
        for(j=1;j<=m;j++)if(end[i][j]==‘1‘)
        {
            p--;
            add(id[i][j]+1,t,1,0);
            add(t,id[i][j]+1,0,0);
        }
    }
    if(p)return 1;
    for(i=1;i<=n;i++)
    {
        scanf("%s",str+1);
        for(j=1;j<=m;j++)
        {
            int flow=str[j]-‘0‘;
            if(!flow)continue;
            int flowa=(sta[i][j]==‘1‘)?(flow+1>>1):(flow>>1);
            int flowb=(end[i][j]==‘1‘)?(flow+1>>1):(flow>>1);
            if(sta[i][j]==‘1‘&&end[i][j]==‘1‘)flowa=flowb=flow>>1;

            add(id[i][j],id[i][j]+1,flowb,1);
            add(id[i][j]+1,id[i][j],0,-1);

            add(id[i][j]+1,id[i][j]+2,flowa,1);
            add(id[i][j]+2,id[i][j]+1,0,-1);
        }
        for(j=1;j<=m;j++)for(k=0;k<8;k++)
        {
            x=i+dx[k],y=j+dy[k];
            if(id[x][y])
            {
                add(id[i][j]+2,id[x][y],inf,0);
                add(id[x][y],id[i][j]+2,0,0);
            }
        }
    }
    return 0;
}
int main()
{
    freopen("test.in","r",stdin);
    if(build())
    {
        puts("-1");
        return 0;
    }
    while(spfa(),dist[t]<inf)
    {
        minfee+=lim[t]*dist[t];
        maxflow-=lim[t];
        handle(lim[t]);
    }
    if(maxflow)puts("-1");
    else cout<<(minfee>>1)<<endl;
    return 0;
}

【BZOJ2668】【cqoi2012】交换棋子 费用流

标签:bzoj2668   cqoi2012   交换棋子   费用流   

原文地址:http://blog.csdn.net/vmurder/article/details/44702813

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!