从这道题学到一种新颖的建模方法。感觉和区间k覆盖的模型有点相似,先去做了poj 3680。那个问题我没能自己想出来,书上的解法很好,但是不能推广到本题。
看了题解,得知需要列出一些式子,用网络流流量平衡来建模。
以样例为例。设第i类志愿者招募x[i]人,则
x[1] >= A[1]
x[1]+x[2] >= A[2]
x[2]+x[3] >= A[3]
求min{C[1]*x[1]+C[2]*x[2]+C[3]*x[3]}。
这是一个线性规划问题。而某些特殊的线性规划可以用网络流解决,就像可以用最短路解决差分约束系统。
在不等号的右边添加d[i]>=0,使它们变成等式。
x[1] = A[1]+d[1] ......(a1)
x[1]+x[2] = A[2]+d[2] ......(a2)
x[2]+x[3] = A[3]+d[3] ......(a3)
就看到这里,后面的变形我想自己处理。
注意到每种志愿者总是覆盖一段区间,因此出现了许多重复的x。或许作差会有用。于是用后面的式子减去前一个,移向得
x[2]+A[1]+d[1] = A[2]+d[2] ......(b2)
x[3]+A[2]+d[2] = x[1]+A[3]+d[3] ......(b3)
式(a1)没有可减的,怎么办呢?出于美学考虑,我决定(a1)-(a3),
x[1]+A[3]+d[3] = x[2]+x[3]+A[1]+d[1] ......(b1)
很高兴地发现(b1)~(b3)满足每个变量在等式的左边和右边各出现一次。于是我就开始写了。
样例的输出为0。
怎么回事呢?画了图,发现这样建图输出不为0只能说明我的最小费用最大流算法写错了。前几天学习高斯消元法,不久便意识到究竟哪里出了错。方程组(a)和(b)并不等价。(a3)-(a1) = ((a2)-(a1))+((a3)-(a2)),(b1)不是独立的。
如何修正?
首先得分析,为什么相邻两式作差会导致每个变量只在左边和右边各出现一次。对于x[i],它第一次出现时,去减前面的,移向后它必然出现在等式左边;它第一次消失时,作差移向后出现在等式右边;中间,它被减掉。
第一天的x在第一天第一次出现,最后一天的x在最后一天的下一天消失。所以,(a1)两边同时减0,用0=0减a(3)的两边即可体现这种变化。
x[1] = A[1]+d[1] ......(b1')
A[3]+d[3] = x[2]+x[3] ......(b4)
原来的3个方程变成了4个,但这没什么要紧的。我们没有丢失信息。
AC,但是CodeVS上比大多数人的代码慢了1s多。
最小费用最大流的算法不够好?但不至于除了我以外大家都写高级算法吧。没有优化读入?加了反而慢了些。
参考hzwer的代码。做法基本相同,但他把方程两边的常数A消去了一边。这样做是正确的,这些边费用为0,本身就优先增广它们。
修改之后时间基本正常了。
某些线性规划问题可以归约到网络流解决。
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int MAXN = 1000, MAXM = 10000, MAXE = (MAXN*2+1+MAXM)*2, MAXV = MAXN+3;
const ll INF = 1LL<<60;
int e_ptr = 2;
int fst[MAXV+5];
struct Edge {
int v, next;
ll c, w;
} E[MAXE+5];
inline void addEdge(int u, int v, ll c, ll w)
{
E[e_ptr] = (Edge){v, fst[u], c, w}; fst[u] = e_ptr++;
E[e_ptr] = (Edge){u, fst[v], 0, -w}; fst[v] = e_ptr++;
}
template <typename Tp>
inline void read(Tp& x)
{
char ch = getchar();
x = 0;
while (ch<'0' || ch>'9')
ch = getchar();
while (ch>='0' && ch<='9') {
x = x*10 + ch - '0';
ch = getchar();
}
}
namespace MCMF {
ll d[MAXV+5];
int p[MAXV+5];
bool inq[MAXV+5];
int SPFA(int s, int t, ll& cost)
{
queue<int> Q;
for (int i = s+1; i <= t; ++i) {
d[i] = INF;
inq[i] = false;
}
d[s] = 0;
inq[s] = true;
Q.push(s);
while (!Q.empty()) {
int u = Q.front();
Q.pop();
inq[u] = false;
for (int i = fst[u]; i; i = E[i].next) {
int v = E[i].v;
if (E[i].c > 0 && d[u] + E[i].w < d[v]) {
d[v] = d[u] + E[i].w;
p[v] = i;
if (!inq[v]) {
inq[v] = true;
Q.push(v);
}
}
}
}
if (d[t] == INF)
return 0;
ll f = INF;
for (int u = t; u != s; u = E[p[u]^1].v)
f = min(f, E[p[u]].c);
for (int u = t; u != s; u = E[p[u]^1].v) {
E[p[u]].c -= f;
E[p[u]^1].c += f;
cost += f * E[p[u]].w;
}
return f;
}
ll MCMF(int s, int t)
{
ll cost = 0;
while (SPFA(s, t, cost))
;
return cost;
}
};
int main()
{
int N, M;
read(N); read(M);
ll a = 0, b;
for (int i = 1; i <= N; ++i) {
read(b);
if (b > a)
addEdge(i, N+2, b-a, 0);
else if (b < a)
addEdge(0, i, a-b, 0);
addEdge(i, i+1, INF, 0);
a = b;
}
addEdge(0, N+1, a, 0);
for (int i = 1; i <= M; ++i) {
int s, t;
ll w;
read(s); read(t); read(w);
addEdge(t+1, s, INF, w);
}
printf("%lld", MCMF::MCMF(0, N+2));
return 0;
}