【BZOJ3550】【ONTAK2010】 Vacation 线性规划转费用流

链接:

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

题解:

我依然只会做,不会证。
如果初学者,可以一览,想深究,请移步。
After alli am a Juruo at present.

      首先我们可以有基础的线性规划:每连续 n 点最多选k个。
转换成数学模型:
(其中 a 数组表示选(1)与不选(0),而 t 数组则是辅助变量,将小于等于关系转化为等于关系)
(至于为什么我不用use啊,choose啊什么的作为变量名——你们看一眼它们在公式中变成啥样就知道了—— use choose )

(ni=1ai)+t1=k
                 
(3ni=2n+1ai)+t2n+1=k
然后我们差分一下这2n+1个式子,保留第一个和最后一个式子,会得到:
k=(ni=1ai)+t1
a1+t1=an+1+t2
a2+t2=an+2+t3
                 
a2n+t2n=a3n+t2n+1
(3ni=2n+1ai)+t2n+1=k

然后关键的时刻到了!我们把每个式子看作一个节点,而边则是式子之间的关系。
那么对于此题我们目前就拥有了3n个项为常数的式子节点,和2个项为 O(n) 的式子节点。(当然还需要两个节点作为超级源和超级汇来控制流量)

点已经明了了,那么边呢?
前面说过边是式子间的关系,而我们不妨把每个等号看作那个节点的本质,那么等号的左边就是节点的入边总容量,而等号的右边就是节点的出边总容量。

然后我们分析每个变量的含义以及定义域,由此来确定每条边(节点之间关系)的费用以及容量。这个应该很显然, a 是取或不取,那么它的容量应该就是简单的 1 费用则是它本身的取值 vali ,而 f 是一个辅助变量,显然容量就应该是 inf ,而费用则是 0 。但是因为题目中的限定“k”,所以我们可以将这个容量设为“ k <script type="math/tex" id="MathJax-Element-52">k</script>”

图建完了,我们还需要超级源点连源点,容量k,费用0,汇点连超级汇点,容量k,费用0来进行总的限制。

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 610
#define M 5000
#define inf 0x3f3f3f3f
using namespace std;
struct Eli
{
    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;
}
inline void ADD(int u,int v,int len,int fee)
{add(u,v,len,-fee),add(v,u,0,fee);}
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,n,m,p,S,T;
int val[N];
bool build()
{
    int i,j,k;
    int x,y;

    scanf("%d%d",&n,&m);
    S=0,T=n*2+1,cnt=1,s=n*2+2,t=n*2+3;
    ADD(s,S,m,0),ADD(T,t,m,0);

    for(i=1;i<=n*3;i++)scanf("%d",&val[i]);
    for(i=1;i<=n;i++)
    {
        ADD(S,i,1,val[i]);
        ADD(i,i+n,1,val[i+n]);
        ADD(i+n,T,1,val[i+(n<<1)]);
    }
    for(i=1;i<=T;i++)ADD(i-1,i,m,0);
    return 0;
}
int main()
{
    freopen("test.in","r",stdin);
    build();
    while(spfa(),dist[t]<inf)
    {
        minfee+=lim[t]*dist[t];
        handle(lim[t]);
    }
    cout<<(-minfee)<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值