BZOJ1061: [Noi2008]志愿者招募 && 单纯形学习笔记

9 篇文章 0 订阅
1 篇文章 0 订阅

题目

http://www.lydsy.com/JudgeOnline/problem.php?id=1061

题解

终于搞完了单纯形,还真不是个简单的东西,但是理解之后,算法挺简单的,证明只有自动略过了qwq。

学习单纯形推荐算导29章,严密又不失详细。

标准的线性规划式子是, A=(aij),b=(bi),c=(ci),x=(xi)
大小的话A为m* n,b为m* 1,c为1* n,a为1 *n.c右上角的T表示转置。

MAX    cTx

Ax<=b

x>=0

会出现一些问题使得它不是个标准型。有以下几种
1.MAX函数是MIN,对目标函数f取负改成MAX即可
2.变量 xi 没有非负约束,那么拆成两个变量 xi=xix′′i ,并且有 xi>=0 x′′i>=0 带回原式即可。
3.是等式约束,不是不等式约束(不等式约束不能是严格不等),如果有 X=b 拆为 X<=b 并且 X>=b 即可。
4.是>=而不是<=两边同时乘一个符号即可。

化成这样之后化成松弛型线性规划式子。(N是非基变量集合,即化成松弛型之后在右边的变量)

弄好了之后,为了方便解,一般要变成一种松弛型的等式形式。即对于第i个约束

j=1naijxj<=b

变成
xi+n=bj=1naijxj

xi+n>=0

即可,后续还会对这个式子进行变形,一个定义,等号左边的变量叫做基本变量,右边的叫非基变量,然后把原函数值用z代替,用N表示非基变量集合,B表示基本变量集合。

z=v+jNcjxj

xi=bijNaijxj(iB)

好像这里需要初始化一下以保证初始基本解可行,因为本题特殊,只需要对偶一下就可以,对偶的具体操作以及后续计算可以看这里–传送

然后反复使用单纯形法,感觉就是不断增加z的表达式中的v,不断拿一个基变量出来和非基变量进行替换(即pivot操作),直到 ci 全部为负数,返回答案。

后续初始化还要学学.学的还很多

以及这道题很神的费用流做法,以及啥是zkw费用流,提及zkw的话还有,zkw线段树qwq。

代码

相当于自己的一个板子啦qwq。

//QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const double eps=1e-7;
const int maxm=10000+10;
const int maxn=1000+10;
const double INF=(1ull<<63);
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}

int n,m;
double c[maxn],b[maxm],cof[maxm][maxn];

//who in ,who out , the ans
inline void Pivot(int in,int out,double &z)
{
    b[out]/=cof[out][in];
    cof[out][in]=1/cof[out][in];
    for(int i=1;i<=n;++i) if(i!=in) cof[out][i]*=cof[out][in];

    for(int i=1;i<=m;++i) if(i!=out && fabs(cof[i][in]) > eps )//fabs(cof[out][in]) > eps 你这样写是会把剪枝去掉然后T到飞起的 
    {
        b[i]-=b[out]*cof[i][in];
        for(int j=1;j<=n;++j) if(j!=in) cof[i][j]-=cof[out][j]*cof[i][in];
        cof[i][in]=-cof[i][in]*cof[out][in];
    }

    z+=c[in]*b[out];//v增加的量
    for(int i=1;i<=n;++i) if(i!=in) c[i]-=c[in]*cof[out][i];
    c[in]=-c[in]*cof[out][in];
}

inline double Simplex()
{
    double z=0;
    while(1)
    {
        int in,out;
        for(in=1;in<=n;++in) if(c[in]>eps) break;
        if(in==n+1) break;//not found

        double jin=INF;//find the YveShu that is the most Jin 
        for(int i=1;i<=m;++i) if(cof[i][in] > eps && b[i]/cof[i][in]<jin)
            jin=b[i]/cof[i][in],out=i;

        if(jin==INF) return INF;
        Pivot(in,out,z);
    }
    return z;
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i) c[i]=read();
    for(int i=1,s,t;i<=m;++i)
    {
        s=read();t=read();
        for(int j=s;j<=t;++j) cof[i][j]=1;
        b[i]=read();
    }
    printf("%lld\n",(long long)(Simplex()+0.5));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值