1061: [Noi2008]志愿者招募
Time Limit: 20 Sec Memory Limit: 162 MB
Submit: 5889 Solved: 3545
[Submit][Status][Discuss]
Description
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难
题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要
Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用
是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这
并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
Input
第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负
整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了
方便起见,我们可以认为每类志愿者的数量都是无限多的。
Output
仅包含一个整数,表示你所设计的最优方案的总费用。
Sample Input
3 3
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
14
HINT
1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。
之前用费用流做过一次,主要是利用等式根据流量平衡建模,加上费用跑费用流来解
这次使用单纯形去解决,同时学习单纯形模板和如何转换对偶问题!
我们用 B[i] 代表第i天需要的人数
A[i][j]代表第i天,第j个人能否工作(取值为0或1)
X[i]代表使用了第i个人的个数
C[i]代表第i个人的花费
那么我们就能得到如下不等式:(假设有两天三个人)
我们的目标函数是:
最小化 :
我们要把这个最小化问题转化为最大化问题,也就是把这个问题转化成他的对偶问题才能用单纯形法去解决!具体过程如下:
因为目标函数的系数都比A大,所以我们可以有如下不等式
更一般的,我们引入变量 y[i]代表第i个方程的系数,得到如下的方程
我们化简一下,变成
那么我们的方程就变为已知:
最大化目标函数:
这样子我们就成功的转化为了对偶问题了,至于为什么可以这样转换,往上一大堆资料,这里只阐述过程~
注意!这里的X都要满足>=0的约束!才可以转换!!!
然后就可以简单粗暴的上单纯形模板了~!
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1500;
const int MAXM=15000;
const double INF=1e10;
const double eps=1e-7;
typedef long long ll;
int N,M;
double A[MAXM][MAXN];//A[i][j]表示第i个方程,Xj的系数是多少
double c[MAXN];//第i个方程需要<=的常数
double b[MAXM];//目标方程Xj的系数是多少
double v;
void Pivot(int l,int e)
{
b[l]/=A[l][e];
for(int i=1;i<=N;i++)
if(i!=e)
A[l][i]/=A[l][e];
A[l][e]=1/A[l][e];
for(int i=1;i<=M;i++)
if(i!=l && fabs(A[i][e])>eps)
{
b[i]-=A[i][e]*b[l];
for(int j=1;j<=N;j++)
if(j!=e)
A[i][j]-=A[i][e]*A[l][j];
A[i][e]=-A[i][e]*A[l][e];
}
v+=c[e]*b[l];
for(int i=1;i<=N;i++)
if(i!=e)
c[i]-=c[e]*A[l][i];
c[e]=-c[e]*A[l][e];
}
double Simplex()
{
int i,l,e;
while(true)
{
for(i=1;i<=N;i++)
if(c[i]>eps)
break;
if((e=i)==N+1)
return v;//答案
double tmp=INF;
for(i=1;i<=M;i++)
if(A[i][e]>eps && b[i]/A[i][e]<tmp)
tmp=b[i]/A[i][e],l=i;
if(tmp==INF)
return INF;//无界
Pivot(l,e);
}
}
int main()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
scanf("%lf",&c[i]);
int x,y;
for(int i=1;i<=M;i++){
scanf("%d%d%lf",&x,&y,&b[i]);
for(int j=x;j<=y;j++)
A[i][j]=1;
}
double ans=Simplex();
printf("%d\n",int(ans+0.5));
return 0;
}