P3980
设第
于是所需费用即
可以看出,这是一个线性规划问题。
由于它是最小化型线性规划,不是标准形式的。因此,可以利用线性规划对偶定理 (证明可以百度等,比如这里) 将其转化为一个最大化型线性规划:
由于 init() 都不用写~)。
具体地过程,就是寻找大于
(其实现在还有一个问题,就是线性规划的最优解会不会出现分数的问题,这个放到最后讲)。
代码:
#include <bits/stdc++.h>
#define N 1034
#define E 10034
using namespace std;
const double eps = 1e-8;
int n, e, l, r;
int i, j;
int id[N + E];
double m[E][N], b[E], *c = m[0], ans[N + E];
void pivot(int r, int c){
int i, j; double coe = 1.0 / m[r][c];
swap(id[n + r], id[c]);
m[r][c] = 1.0;
for(j = 1; j <= n; ++j)
m[r][j] *= coe;
b[r] *= coe;
for(i = 0; i <= e; ++i){
if(i == r) continue;
coe = m[i][c]; m[i][c] = 0.0;
for(j = 1; j <= n; ++j)
m[i][j] -= coe * m[r][j];
b[i] -= coe * b[r];
}
}
bool simplex(){
int i, j, basic, free; double G;
for(; ; ){
basic = free = 0; G = INFINITY;
for(i = 1; i <= n; ++i) // free (nonbasic) variable
if(c[i] > c[free]) free = i;
if(!free) return true;
for(j = 1; j <= e; ++j) // basic variable
if(m[j][free] > eps && b[j] < G * m[j][free]){
G = b[j] / m[j][free]; basic = j;
}
if(!basic) return puts("Unbounded"), false;
pivot(basic, free);
}
}
int main(){
scanf("%d%d", &n, &e);
for(j = 1; j <= n; ++j) scanf("%lf", c + j);
for(i = 1; i <= e; ++i){
scanf("%d%d%lf", &l, &r, b + i);
for(j = l; j <= r; ++j)
m[i][j] = 1.0;
}
if(simplex())
printf("%.lf\n", -*b);
return 0;
}
(听说这题还可以用最小费用最大流做?等待楼上来填坑)
接下来一个小细节就是证明上述线性规划必有整数解。
我们知道,线性规划的问题的最优解为整数的一个必要条件是它的任意一个子方阵的行列式为
而这是一个 0/1 方阵,且每一行中的
我们先用第
其中每一行有左边一个
1 0 0 -1 0 0 0
1 0 0 0 0 -1 0
0 1 0 0 -1 0 0
0 1 0 0 0 0 -1
0 0 1 0 0 -1 0
0 0 0 1 0 0 0
0 0 0 0 1 -1 0
我们将这些行按照
接下来,对于
1 0 0 -1 0 0 0
0 1 0 0 -1 0 0
0 0 1 0 0 -1 0
0 0 0 1 0 -1 0
0 0 0 0 1 -1 0
0 0 0 0 0 1 -1
0 0 0 0 0 0 1
最终情况下,它要么是一个上三角矩阵,则原行列式显然为