用“道”的思想解决费用流问题---取/不取皆是取 (有下界->有上界) / ACdreamoj 1171

题意: 给一个矩阵,给出约束:i(0<i<n)行至少去ai个数,j行至少取bi个数,要求取的数值之和最小。

开始一见,就直接建了二分图,但是,发现这是有下界无上界最小费用流问题,肿么办。。。问题转化:所谓正难则反!现在某行/列要至少取k个,总和最小,不就是那行/列最多留下K个,使留下的和最大?其实也就是最多取k个,使值最大,转化为下界为0,有上界的最大费用问题(普通问题)。“取”,“不取”,本质都是一样的,正是“无为”的思想!取,则最小;不取,最大。道也。道之道非常道,名可名非常名~~

还有一点,转化之后,最大费用时未必最大流。解决方法有二:

其一:每次增广后,加判断,若费用开始递减,则跳出,此时取最大。(据说二分图费用是先增后减函数:每次增广,当费用最大的时候,但是这时候流量不是最大,所以减小费用来增大流量,不知道一般图是不是。。。)

其二:释放法,X部所有点直接向汇点连边,费用0,流量Inf,我感觉这样,当X部还有流量的时候,直接就向汇点释放了,所有必是最大费用(不会再减少了)。

俩种方法我都试过,AC。

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<string>
using namespace std;
const int maxv=200;
const int maxe=200*200*2+800;
const int inf=0x3f3f3f3f;
int nume=0;int e[maxe][4];int head[maxv];
int n,m;int ss,tt;
int val[105][105];
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[maxv];int pre[maxv];int prv[maxv];
int d[maxv];
bool spfa(int &sum,int &flow)
{
    for(int i=0;i<=tt;i++)
          {
              inq[i]=0;
              d[i]=inf;
          }
    queue<int>q;
    q.push(ss);
    inq[ss]=1;
    d[ss]=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[cur]+e[i][3]<d[v])
            {
                d[v]=d[cur]+e[i][3];
                pre[v]=i;
                prv[v]=cur;
                if(!inq[v])
                {
                    q.push(v);
                    inq[v]=1;
                }
            }
        }
    }
    if(d[tt]==inf)return 0;
    int cur=tt;
    int minf=inf;
    while(cur!=ss)
    {
        int fe=pre[cur];
        minf=e[fe][2]<minf?e[fe][2]:minf;
        cur=prv[cur];
    }
     cur=tt;
    while(cur!=ss)
    {
        e[pre[cur]][2]-=minf;
        e[pre[cur]^1][2]+=minf;
        cur=prv[cur];
    }
    flow+=minf;
    sum+=d[tt]*minf;
    return 1;
}
int mincost(int &flow)
{
    int sum=0;
   // int lastsum=0;
   while(spfa(sum,flow))
   {
       ;
     //  if(-lastsum>-sum)return lastsum;                  //取最值法
      //  lastsum=sum; // cout<<sum<<endl;
   }
    return sum;
}
int sum_all=0;
void init()
{
    nume=0; sum_all=0;
    ss=n+m; tt=n+m+1;
    for(int i=0;i<=tt;i++)
       head[i]=-1;
}
void read_build()
{
    for(int i=0;i<n;i++)
     for(int j=0;j<m;j++)
         {
             scanf("%d",&val[i][j]);
             sum_all+=val[i][j];
             adde(i,j+n,1,-val[i][j]);
         }
      int aa;
     for(int i=0;i<n;i++)
     {
        scanf("%d",&aa);
        adde(ss,i,m-aa,0);
        adde(i,tt,m-aa,0);                            //X部直接向汇点连边(容量够释放就行)
     }
       for(int i=0;i<m;i++)
     {
        scanf("%d",&aa);
        adde(i+n,tt,n-aa,0);
     }
   /*  for(int i=0;i<=m+n+1;i++)
       for(int j=head[i];j!=-1;j=e[j][1])
       {
           printf("%d->%d:f %dw %d\n",i,e[j][0],e[j][2],e[j][3]);
       }*/
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        init();
        read_build();
        int flow=0;
        int ans=sum_all+mincost(flow);
         printf("%d\n",ans);
    }
    return 0;
}

用“道”的思想解决费用流问题---取/不取皆是取 (有下界->有上界) / ACdreamoj 1171,布布扣,bubuko.com

时间: 2024-10-15 01:20:32

用“道”的思想解决费用流问题---取/不取皆是取 (有下界->有上界) / ACdreamoj 1171的相关文章

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

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

HDU 5045 费用流求最大权

点击打开链接 题意:有n个人和m到题目,每个人做对的概率以矩阵形式给出,问如何分配才可以使做对的概率最大,有一个限制条件是做到目前为止每两个人的做题数量差距不能超过1,也就是前n道题目,必须一人做一个 思路:网上都是dp多一点,用网络流也可以,不过麻烦很多,可是本弱是一点dp都不会的选手啊,只能用网络流了,对于那个限制条件,我们可以以前n道题建一次图,然后再来n个,不过就直接建完就可以了,然后我们要求的是什么呢,很明显是最大权,而最大费用最大流刚好可以解决,这里面的费用流有两种方法,用spfa找

P2488 [SDOI2011]工作安排 费用流

\(\color{#0066ff}{ 题目描述 }\) 你的任务是制定出一个产品的分配方案,使得订单条件被满足,并且所有员工的愤怒值之和最小.由于我们并不想使用Special Judge,也为了使选手有更多的时间研究其他两道题目,你只需要输出最小的愤怒值之和就可以了. \(\color{#0066ff}{输入格式}\) \(\color{#0066ff}{输出格式}\) 仅输出一个整数,表示最小的愤怒值之和. \(\color{#0066ff}{输入样例}\) 2 3 2 2 2 1 1 0 0

费用流 hdu3667 Transportation

传送门:点击打开链接 题意:n个节点m条有向边,每条有向边的容量是C,且费用是a*x^2,x是流量,问从1运送k流量到n的最小费用 一般做的费用流边的费用都是固定的,而这题并不是固定的. 但是,看到了C<=5,其实就是在提示可以拆边.. 假如C是3,我们就可以把一条边拆成3条边. 假如不拆,如果通过的流量是1,2,3,那么费用分别是a,4a,9a 如果拆成3条边,那么3条边的费用分别是a,3a,5a,容量都是1 这样就完美的解决了边的费用问题了~ #include<map> #inclu

POJ训练计划2516_Minimum Cost(网络流/费用流)

解题报告 题意: 有n个商店,m个提供商,k种商品</span> n*k的矩阵,表示每个商店需要每个商品的数目: m*k矩阵,表示每个提供商拥有每个商品的个数 然后对于每个物品k,都有n*m的矩阵 i行j列表示 从j提供商向i商店运送一个k商品的代价是多少 判断所有的仓库能否满足所有客户的需求,如果可以,求出最少的运输总费用 思路: 建图的题,不能直接把所有信息建成图,因为n和m跟k都有关系,如果那样子建图的话,就要把k种拆成m类,每个仓库连向该仓库的第k种,然后再和n连线,有费用, 不过这样

【bzoj1150】[CTSC2007]数据备份Backup 模拟费用流+链表+堆

题目描述 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣.已知办公楼都位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份.然而,网络电缆的费用很高.当地电信公司仅能为你提供 K 条网络电缆,这意味着你仅能为 K 对办公楼(或总计2K个办公楼)安排备份.任一个办公楼都属于唯一的配

【bzoj3280】小R的烦恼 费用流

题目描述 小R最近遇上了大麻烦,他的程序设计挂科了.于是他只好找程设老师求情.善良的程设老师答应不挂他,但是要求小R帮助他一起解决一个难题. 问题是这样的,程设老师最近要进行一项邪恶的实验来证明P=NP,这个实验一共持续n天,第i天需要a[i]个研究生来给他搬砖.研究生毕竟也是人,所以雇佣研究生是需要钱的,机智的程设老师已经联系好了m所大学,第j所大学共有l[j]个研究生,同时雇佣这所大学的一个研究生需要p[j]元钱. 本来程设老师满心欢喜的以为,这样捡最便宜的max{a[i]}个研究生雇来,就

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

题意: 一个数字矩阵,可以出发K次,每次可以从右边或者下面走,要求(在收益最大情况下)覆盖全图,不能则输出-1.(规则:每次跳一步的时候若格子数字相等则获得该数字的能量,每跳一步消耗距离的能量).每个格子走且仅能走一次. 选<=K条路径,最优情况来覆盖全图. 显然用拆点为二分图. 一种解法:边(流量,费用) 源点向X部连边(1,0)Y部向汇点连边(1,0)X到Y,若能到,则有边(1,消耗-获得).关键点(解决每个点都覆盖,恰好起到填补的作用):在X部最上面添加一个点,源点连之(k,0)它向所有Y

BZOJ 2879 美食节(费用流-动态加边)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2879 题意:有n道菜,每道菜需要b[i]份,m个厨师,第j个厨师做第i道菜需要时间a[i][j],求做完所有菜,所有人等待的最小总时间. 思路:设所有的菜为sum.一个明显的思路是将每个厨师拆成sum个点.然后sum个菜每个菜向每个厨师的每个点连边,表示该道菜为该厨师第几个做.由于这样数据太大.动态加边.每次增光一次后找到此次增广的厨师,每道菜将其连边. struct node { i