hiho1424 Asa's Chess Problem ( 上下界费用流 )

题目暂无链接
( 北京2016区域赛C题 )

题目大意

给出一个N×N的01矩阵(N<=50,且N为偶数)。有N*N/2对可交换格子,每个格子有且仅有一个可交换对象。并且,每对可交换的格子必然在同一行或同一列。每交换一次的代价是1。对于每一行和每一列,都有一个1的个数的上界和下界。
问,最少执行多少次交换操作,可以满足所有行和列的1的个数的要求,

解答

比较明显的网络流构图,需要用到上下界,注意构图点数不要太多,否则会TLE,该压缩的必须要压缩。

把每一行每一列都看成一个状态,由于可交换的格子必然在同一行或同一列,所以每次交换必然只是从一个状态流向另一个状态。所以,我们可以先预处理出开始状态每一行每一列有多少个1( 记为g[i] ),从源点S向状态i (行或列)连一条上界下界均为g[i],费用为0的边。每个状态i都向汇点T连一条上下界为读入要求,费用为0的边。然后,统计出N*N/2对交换格子交换后由状态i 到状态 j的个数( 先用二维数组压缩统计,记为swg[i][ j ] ),状态i向状态j连一条下界为0,上界为swg[i][ j ],费用为1的边。

根据上述的构图,新添超级源点SS和超级汇点ST,根据模型,转化为下界均为0的网络流。最后,直接求解费用流即可。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <bitset>

#define bll long long
#define dou double
#define For(i,a,b) for (int i=(a),_##i=(b); i<=_##i; i++)
#define Rof(i,a,b) for (int i=(a),_##i=(b); i>=_##i; i--)
#define rep(i,a,b) for (int i=(a),_##i=(b); i<=_##i; i++)
#define rek(i,a,b) for (int i=(a),_##i=(b); i>=_##i; i--)
#define Mem(a,b) memset(a,b,sizeof(a))
#define Cpy(a,b) memcpy(a,b,sizeof(b))
//__builtin_popcountll

using std::sort;
using std::max;
using std::min;
using std::abs;
using std::swap;

// 费用流模板,封装在namespace里,避免变量,函数冲突
namespace fyl
{
    const int maxnode=100+100,maxedge=100*100+100*4+10;
    const int oo=1<<30;
    struct Edge
    {
        int node,next,flow,cost;
        Edge(int a=0,int b=0,int c=0,int d=0)
        {
            node=a,next=b,flow=c,cost=d;
        }
    }E[maxedge*2];
    int cnt;                    // 边数统计
    int head[maxnode],path[maxnode],pre[maxnode],F[maxnode];
    int o[200000];              // spfa队列,开的较大,如果不够可改成循环队列
    bool vis[maxnode];          // spfa判断是否点在队列里

    void init()                 // 初始化,多组数据必须调用
    {
        cnt=0;
        memset(head,255,sizeof head);
    }
    void add_edge(int x,int y,int f,int c)  // 建边,使用频率很高,可以考虑using
    {
        E[cnt]=Edge(y,head[x],f,c),head[x]=cnt++;
        E[cnt]=Edge(x,head[y],0,-c),head[y]=cnt++;
    }
    bool spfa(int S,int T,int N)            // 找一条从S到T的最小费用路径,N是网络流结点最后一个编号
    {
        for (int i=0; i<=N; i++) F[i]=oo;
        int h=0,t=0;
        o[t]=S; F[S]=0; vis[S]=1;
        for (; h<=t; h++)
        {
            int x=o[h];
            for (int i=head[x]; i!=-1; i=E[i].next)
            {
                int y=E[i].node;
                if (E[i].flow>0 && F[x]+E[i].cost<F[y])
                {
                    F[y]=F[x]+E[i].cost;
                    pre[y]=x;
                    path[y]=i;
                    if (!vis[y]) vis[o[++t]=y]=1;
                }
            }
            vis[x]=0;
        }
        return F[T]!=oo;
    }
    void get_ans(int S,int T,int &flow,int &cost)   // 找到路径后,得到流量和费用
    {
        int minf=oo;
        for (int x=T; x!=S; x=pre[x])
            minf=min(minf,E[path[x]].flow);
        for (int x=T; x!=S; x=pre[x])
        {
            E[path[x]].flow-=minf;
            E[path[x]^1].flow+=minf;
            cost+=E[path[x]].cost*minf;
        }
        flow+=minf;
    }
    void solve(int S,int T,int N,int &flow,int &cost)   // 建图后求最小费用最大流
    {
        while(spfa(S,T,N))
            get_ans(S,T,flow,cost);
    }
}

const int maxn=100+10;
int N;
int A[maxn][maxn],u[maxn],d[maxn];
int g[maxn],swg[maxn][maxn];

int Done()
{
    int S=0,T=2*N+1,num=2*N+1;  // 2*N个状态,表示行和列,num表示点的编号
    int SS=++num,ST=++num;
    int sumdown=0;              // 下届的和
    fyl::init();                // 初始化
    Mem(g,0);                   // 统计初始矩阵每个状态的1的个数
    Mem(swg,0);                 // 统计状态间的交换次数,用于压缩边
    For(i,1,N)
        For(j,1,N)
        {
            scanf("%d",&A[i][j]);
            g[i]+=A[i][j];
            g[j+N]+=A[i][j];
        }
    For(i,1,2*N) scanf("%d%d",&d[i],&u[i]); // 读入行列上下界
    For(i,1,N*N/2)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        if (A[x1][y1]!=A[x2][y2])
        {
            if (A[x1][y1]==0) swap(x1,x2),swap(y1,y2);
            if (x1==x2) 
                swg[y1+N][y2+N]++;
            else
                swg[x1][x2]++;
        }
    }
    using fyl::add_edge;                // 频繁出现,先using
    For(i,1,2*N)
    {
        if (g[i])
        {
            add_edge(SS,i,g[i],0);
            add_edge(S,ST,g[i],0);
            sumdown+=g[i];
        }
        if (d[i]>0)
        {
            add_edge(SS,T,d[i],0);
            add_edge(i,ST,d[i],0);
            u[i]-=d[i];
            sumdown+=d[i];
        }
        if (u[i]) add_edge(i,T,u[i],0);
    }
    For(i,1,2*N)
        For(j,1,2*N)
            if (swg[i][j]) add_edge(i,j,swg[i][j],1);
    add_edge(T,S,fyl::oo,0);
    int flow=0,cost=0;
    fyl::solve(SS,ST,num,flow,cost);
    if (flow!=sumdown) return -1;   // 最大流要等于下届可,否则无解
    return cost;                    // 返回有解的情况下的最小费用
}


int main(int argc, char* argv[])
{
    for (; scanf("%d",&N)!=EOF; )
    {
        int ans=Done();
        printf("%d\n",ans);
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容