hdu4862 2014多校B题/ 费用流(最优情况下用不大于K条路径覆盖)(不同的解法)

题意: 一个数字矩阵,可以出发K次,每次可以从右边或者下面走,要求(在收益最大情况下)覆盖全图,不能则输出-1。(规则:每次跳一步的时候若格子数字相等则获得该数字的能量,每跳一步消耗距离的能量)。每个格子走且仅能走一次。

选<=K条路径,最优情况来覆盖全图。

显然用拆点为二分图。

一种解法:边(流量,费用)

源点向X部连边(1,0)Y部向汇点连边(1,0)X到Y,若能到,则有边(1,消耗-获得)。关键点(解决每个点都覆盖,恰好起到填补的作用):在X部最上面添加一个点,源点连之(k,0)它向所有Y点连边(1,0)。跑最小费用最大流即可。

第二种:(感想zz1215提供的建图思路)

源点向X部连边(1,0)Y部向汇点连边(1,0),Y到X,若能到,则有边(1,消耗-获得)(注意这里是回流),每个点I-->I`有边(1,-w_inf),这里的w_inf为相对大数,只要保证该费用较“小”即可(相对其他费用,他是最廉价的,这样必优先流这条边。添加超级源点,向源点连边(K,0)。增广K次中,若一直增大,则取最大,否则到开始下降的时候要BREAK。(先曾后减的)。

PS:开始时候因为定位编号搞错有没有!编号(i,j)=i*m+j,而不是i*n+j!!!

图:

代码:

#include<iostream>              //24ms
#include<cstdio>
#include<algorithm>
#include<queue>
#include<string>
using namespace std;
int n,m,k;
const int inf=0x3f3f3f3f;
int a[25][25];
int head[500];int e[10000][4];int nume=0;
void inline adde(int i,int j,int c,int w)
{
    e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;
    e[nume][2]=c;e[nume++][3]=w;
    e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;
    e[nume][2]=0;e[nume++][3]=-w;
}
int inq[500];int d[500];
bool spfa(int &sumcost)
{
    for(int i=0;i<=2*n*m+3;i++)
    {
        inq[i]=0;d[i]=inf;
    }
    int minf=inf;
    queue<int>q;
    int prv[300];int pre[300];
    q.push(2*n*m+2);
    inq[2*n*m+2]=1;
    d[2*n*m+2]=0;
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();inq[cur]=0;
        for(int i=head[cur];i!=-1;i=e[i][1])
        {
            int v=e[i][0];
            if(e[i][2]>0&&d[v]>e[i][3]+d[cur])
            {
                d[v]=e[i][3]+d[cur];
                prv[v]=cur;
                pre[v]=i;
                if(!inq[v])
                {
                    q.push(v);
                    inq[v]=1;
                }
            }
        }
    }
    if(d[2*n*m+1]==inf)return 0;
    int cur=2*n*m+1;
    while(cur!=2*n*m+2)
    {
        minf=min(minf,e[pre[cur]][2]);
        cur=prv[cur];
    }
    cur=2*n*m+1;
    while(cur!=2*n*m+2)
    {
        e[pre[cur]][2]-=minf;e[pre[cur]^1][2]+=minf;
        cur=prv[cur];
    }
    sumcost+=d[2*n*m+1]*minf;
    return 1;
}
int mincost()
{
    int sum=0;
    while(spfa(sum));
    return sum;
}
void init()
{
    nume=0;
    for(int i=0;i<=2*n*m+3;i++)
    {
        head[i]=-1;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int iii=1;iii<=T;iii++)
    {
        scanf("%d%d%d",&n,&m,&k);
        init();
        string s;
       for(int i=0;i<n;i++)
       {
           cin>>s;
            for(int j=0;j<m;j++)
           {
             a[i][j]=s[j]-'0';
            }
       }
        printf("Case %d : ",iii);

        if(min(n,m)>k)
         {
             printf("-1\n");continue;
         }
        for(int i=0;i<n;i++)                           //起点2*n*m+2,终点2*n*m+1
          for(int j=0;j<m;j++)
          {
              for(int ii=i+1;ii<n;ii++)
                 {
                     int temp=(a[i][j]==a[ii][j]?a[i][j]:0);
                     adde(m*i+j,m*ii+j+n*m+1,1,ii-i-1-temp);
                 }

              for(int jj=j+1;jj<m;jj++)
                  {
                      int temp=(a[i][j]==a[i][jj]?a[i][j]:0);
                      adde(m*i+j,m*i+jj+n*m+1,1,jj-j-1-temp);
                  }
          }
        for(int i=0;i<n*m;i++)
           adde(2*n*m+2,i,1,0);
        adde(2*n*m+2,n*m,k,0);
        for(int i=n*m+1;i<=2*n*m;i++)
        {
           adde(n*m,i,1,0);
           adde(i,2*n*m+1,1,0);
        }
     /*  for(int i=0;i<=2*n*m+2;i++)
          for(int j=head[i];j!=-1;j=e[j][1])
            printf("%d->%d:c:%d,w:%d\n",i,e[j][0],e[j][2],e[j][3]);*/
        printf("%d\n",-mincost());
    }
    return 0;
}

方法二:

#include<iostream>           //31ms
#include<cstdio>
#include<queue>
#include<string>
using namespace std;
int n,m,k;
const int inf=0x3f3f3f3f;
const int  winf=100000;
 int a[25][25];
int head[500];int e[20001][4];int nume=0;
void inline adde(int i,int j,int c,int w)
{
    e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;
    e[nume][2]=c;e[nume++][3]=w;
    e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;
    e[nume][2]=0;e[nume++][3]=-w;
}
int inq[500];int d[500];
bool spfa(long long &sumcost)
{
    for(int i=0;i<=2*n*m+3;i++)
    {
        inq[i]=0;d[i]=inf;
    }
    int prv[500];int pre[500];
    int minf=inf;
    queue<int>q;
    q.push(n*m);
    inq[n*m]=1;
    d[n*m]=0;
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();
        inq[cur]=0;
        for(int i=head[cur];i!=-1;i=e[i][1])
        {
            int v=e[i][0];
            if(e[i][2]>0&&d[v]>e[i][3]+d[cur])
            {
                d[v]=e[i][3]+d[cur];
                prv[v]=cur;
                pre[v]=i;
                if(!inq[v])
                {
                    q.push(v);
                    inq[v]=1;
                }
            }
        }
    }
    if(d[2*n*m+1]==inf)return 0;
    int cur=2*n*m+1;
    while(cur!=n*m)
    {
        minf=min(minf,e[pre[cur]][2]);
        cur=prv[cur];
    }
    cur=2*n*m+1;
    while(cur!=n*m)
    {
        e[pre[cur]][2]-=minf;
        e[pre[cur]^1][2]+=minf;
        cur=prv[cur];
    }
    sumcost+=d[2*n*m+1]*(long long)minf;
    return 1;
}
long long mincost()
{
    long long sum=0;
    long long lastsum=0;
    while(spfa(sum))                         //变小的时候跳出
    {
        if(lastsum>=-sum){return -lastsum;}
        lastsum=-sum;
    }
    return sum;
}
void init()
{
    nume=0;
    for(int i=0;i<=2*n*m+3;i++)
    {
        head[i]=-1;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int iii=1;iii<=T;iii++)
    {
        scanf("%d%d%d",&n,&m,&k);
        init();
        string s;
       for(int i=0;i<n;i++)
       {
           cin>>s;
            for(int j=0;j<m;j++)
           {
            a[i][j]=s[j]-'0';
            }
       }
        printf("Case %d : ",iii);

        if(min(n,m)>k)
         {
             printf("-1\n");continue;
         }
        for(int i=0;i<n;i++)                      //起点n*m,终点:2*n*m+1
          for(int j=0;j<m;j++)
          {
              for(int ii=i+1;ii<n;ii++)
                 {
                    int temp=(a[i][j]==a[ii][j]?a[i][j]:0);
                    adde(m*i+j+n*m+1,m*ii+j,1,ii-i-1-temp);
                 }
              for(int jj=j+1;jj<m;jj++)
                  {
                       int temp=(a[i][j]==a[i][jj]?a[i][j]:0);
                      adde(m*i+j+n*m+1,m*i+jj,1,jj-j-1-temp);
                  }
          }
        for(int i=0;i<n*m;i++)
           adde(2*n*m+2,i,1,0);
        adde(n*m,n*m*2+2,k,0);
        for(int i=n*m+1;i<=2*n*m;i++)
        {
           adde(i-n*m-1,i,1,-winf);
           adde(i,2*n*m+1,1,0);
        }
     /* for(int i=0;i<=2*n*m+2;i++)
          for(int j=head[i];j!=-1;j=e[j][1])
            printf("%d->%d:c:%d,w:%d\n",i,e[j][0],e[j][2],e[j][3]);*/
        cout<<-mincost()-n*m*winf<<endl;
    }
    return 0;
}

hdu4862 2014多校B题/ 费用流(最优情况下用不大于K条路径覆盖)(不同的解法),布布扣,bubuko.com

时间: 2024-12-28 01:50:35

hdu4862 2014多校B题/ 费用流(最优情况下用不大于K条路径覆盖)(不同的解法)的相关文章

HDU 4862 Jump 费用流

又是一个看了题解以后还坑了一天的题…… 结果最后发现是抄代码的时候少写了一个负号. 题意: 有一个n*m的网格,其中每个格子上都有0~9的数字.现在你可以玩K次游戏. 一次游戏是这样定义的: 你可以选任意之前没有走过的格子作为起点.然后走任意步,其中每一步你可以向右或者向下走任意格.假如从(x1, y1)走到(x2, y2)需要花费能量|x1-x2|+|y1-y2|-1,如果这一步和上一步格子的数字相同,那么可以获得格子上相应数字的能量.能量可以为负值. 问你,在K次以内走完所以格子最多能得到多

【Tyvj1982】武器分配(费用流)

题意:有N个人要从A个物品中各取一个,B个物品中各取一个,选取第i个A类物品和第j个B类物品的费用是(a[i]-b[j])^2 求最小总花费 n<=a,b<=80 a[i],b[i]<=10000 思路:第一题费用流 由源点到每个A类物品连容量为1,费用为0的边 每个B类物品到第一个汇点连容量为1,费用为0的边 对于ai和bj连容量为1,费用为(a[i]-b[j])^2的边 因为只需要N对物品,由第一个汇点到第二个汇点连容量为N,费用为0的边来限制流量上限 答案就是从源点到第二个汇点流量

【进阶——最小费用最大流】hdu 1533 Going Home (费用流)Pacific Northwest 2004

题意: 给一个n*m的矩阵,其中由k个人和k个房子,给每个人匹配一个不同的房子,要求所有人走过的曼哈顿距离之和最短. 输入: 多组输入数据. 每组输入数据第一行是两个整型n, m,表示矩阵的长和宽. 接下来输入矩阵. 输出: 输出最短距离. 题解: 标准的最小费用最大流算法,或者用KM算法.由于这里是要学习费用流,所以使用前者. 最小费用最大流,顾名思义,就是在一个网络中,不止存在流量,每单位流量还存在一个费用.由于一个网络的最大流可能不止一种,所以,求出当前网络在流量最大的情况下的最小花费.

【费用流】BZOJ1221-[HNOI2001] 软件开发

[题目大意] 某软件公司正在规划一项n天的软件开发计划,根据开发计划第i天需要ni个软件开发人员,为了提高软件开发人员的效率,公司给软件人员提供了很多的服务,其中一项服务就是要为每个开发人员每天提供一块消毒毛巾,这种消毒毛巾使用一天后必须再做消毒处理后才能使用.消毒方式有两种,A种方式的消毒需要a天时间,B种方式的消毒需要b天(b>a),A种消毒方式的费用为每块毛巾fA, B种消毒方式的费用为每块毛巾fB,而买一块新毛巾的费用为f(新毛巾是已消毒的,当天可以使用):而且f>fA>fB.公

bzoj 2324 [ZJOI2011]营救皮卡丘(floyd,费用流)

2324: [ZJOI2011]营救皮卡丘 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1777  Solved: 712[Submit][Status][Discuss] Description 皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路. 火箭队一共有N个据点,据点之间存在M条双向道路.据点分别从1到N标号.小智一行K人从真新镇出发,营救

【bzoj4930】棋盘 费用流

题目描述 给定一个n×n的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置(x,y),(u,v)能互相攻击当前仅 当满足以下两个条件: 1:x=u或y=v 2:对于(x,y)与(u,v)之间的所有位置,均不是障碍. 现在有q个询问,每个询问给定ki,要求从棋盘中选出ki个空位置来放棋子,问最少互相能攻击到的棋子对数是多少? 输入 第一行一个整数n. 接下来输入一个n×n的字符矩阵,一个位置若为.,则表示这是一个空位置,若为#,则为障碍. 第n+2行输入一个整数q代表询问个数. 接下来q

【bzoj2879】[Noi2012]美食节 费用流+动态加边

原文地址:http://www.cnblogs.com/GXZlegend 题目描述 CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得自己桌上没有已经摆在别人餐桌上的美食是一件无法忍受的事情.于是小M开始研究起了做菜顺序的问题,即安排一个做菜的顺序使得同学们的等待时间最短.小M发现,美食节共有n种不同的菜品.每次点

洛谷 1004 dp或最大费用流

思路: dp方法: 设dp[i][j][k][l]为两条没有交叉的路径分别走到(i,j)和(k,l)处最大价值. 则转移方程为 dp[i][j][k][l]=max(dp[i-1][j][k-1][l],dp[i][j-1][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k][l-1])+map[i][j]+map[k][l]; 若两点相同减去一个map[i][j]即可 费用流方法(可以扩展为k条路径,但时间复杂度较高): 源点连接左上角点流量为k.费用为0,右下角

让菜鸡讲一讲费用流(EK)

让我再讲一个故事吧. 又有一些小精灵要准备从银月城(S)迁徙到Nibel山(T). 这两个地方之间的道路构成了一个网络. 每个道路都有它自己的容量,这决定了每天有多少小精灵可以同时从这儿通过. 和上一篇不同的是,由于上次迁徙的规模很大, 吸引了其它一些种族的注意, 这次每条道路都会有一些人/兽人/哥布林/...向精灵们征收过路费, 现在精灵们想知道,在花费最小的情况下,它们迁徙的速度最大是多少只每天. 费用流=最小费用最大流 在要求流最大的情况下要求费用最小,好像原来的isap已经派不上用场了呢