这道题目可以直接用单纯型对偶问题水过去,而且代码超短不到1k。。还是先讲一下费用流做法(但是并没有写):
我推荐网上一种比较简单的方法(传送门),不过没有证明,我会在下面给出。
首先从源点S给出一个很大的流F,连向表示第一天的点容量为F,然后对于第i天,连边i→i+1,容量为F-a[i],费用为0,特殊地,第n天连边n→T,容量为F-a[n],费用为0。我们定义这时第i个点连向第i+1个点的边为该点的出边。
然后我们的目标是使到T的流量最终达到F,即每一天的人数的足够。不妨考虑一种志愿者i,如果雇佣xi个,则这xi个志愿者可以将第Si到Ti每一个点的出边的容量+xi,同时需要费用Ci*xi。实际上Si到Ti的每一个点的出边容量+xi就相当于从Si到Ti+1(Tn+1相当于汇点T)连一条容量为xi的边。但是xi我们是不知道的,是根据需要得到的,因此我们可以连边Si→Ti+1,容量为inf,费用为Ci。定义这样的边为新边。然后跑一遍最小费用最大流即可。显然最大流为F(可行的话),那么此时的最小费用流即最小的花费。
简单证明一下正确性:显然这样的解是最优解;然后证明一下可行性:假设当最大流为F时,还有i没有满足人数约束,我们找到一个i,如果i没有满足人数约束,那么将所有跨越i的的新边拆成两条,一条先到i,一条再从i出发,那么显然答案不变。由于i不满足人数约束,那么显然从i出发的新边(包括拆好的)的容量之和<a[i],而i的出边容量为F-a[i],因此从i出发的容量之和<a[i]+F-a[i]=F,那么汇点T的容量显然不满足F,矛盾! 证毕
然后就可以跑最小费用最大流了。。点O(N),边O(N+M)还是很快的。。期望复杂度O(FM)(woc~要爆炸)
如果用线性规划做。。就很简单了,实际上是最小化一个东西,满足若干约束,而且形式是Σaixi>=b。这实际上就是一个标准型现行规划的对偶问题,转化一下就好了(具体见《算法导论》)
AC代码如下:
#include<iostream> #include<cstdio> #define eps 1e-6 using namespace std; int n,m; double a[10005][1005]; void pivot(int x,int y){ a[x][y]=1/a[x][y]; int i,j; for (i=1; i<=n+1; i++) if (i!=y) a[x][i]*=a[x][y]; for (i=1; i<=m+1; i++) if (i!=x) for (j=1; j<=n+1; j++) if (j!=y) a[i][j]-=a[i][y]*a[x][j]; for (i=1; i<=m+1; i++) if (i!=x) a[i][y]*=-a[x][y]; } void simplex(){ int i,j,k=0; a[m+1][0]=0; while (1){ for (i=1; i<=n; i++) if (a[m+1][i]>a[m+1][k]) k=i; if (!k) return; j=1; for (i=2; i<=m; i++) if (a[i][k]>eps) if (a[i][n+1]*a[j][k]<a[j][n+1]*a[i][k]) j=i; pivot(j,k); k=0; } } int main(){ scanf("%d%d",&n,&m); int i,j; for (i=1; i<=n; i++) scanf("%lf",&a[m+1][i]); for (i=1; i<=m; i++){ int x,y; scanf("%d%d%lf",&x,&y,&a[i][n+1]); for (j=x; j<=y; j++) a[i][j]=1; } simplex(); printf("%.0f\n",-a[m+1][n+1]); return 0; }
by lych
2016.2.20