【bzoj4842】[Neerc2016]Delight for a Cat 线性规划与网络流
题目描述
输入
输出
样例输入
10 4 1 2
1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
样例输出
69
EEESESEESS
题解
线性规划与网络流
首先设 $x_i$ 表示第 $i$ 个位置是否填入 S ,是则为 1 ,否则为 0 。
那么先 “钦定” 所有的都为 $e_i$ ,把 $\sum\limits_{i=1}^ne_i$ 看作基本收益,然后再考虑 $x_i=1$ 的贡献,相当于 $x_i$ 为 1 时能够获得 $s_i-e_i$ 的收益。
因此我们要做的就是:最大化 $\sum\limits_{i=1}^n(s_i-e_i)$ ,满足对于任意的 $p$ 均有 $t1\le\sum\limits_{i=p}^{p+k-1}x_i\le k-t2$ 。
此时由于 $x_i$ 仅为 1 或 0 的限制,不能使用单纯形。
进一步推导,添加辅助变量 $y_p$ 和 $z_p$ ,将限制条件转变为等式关系:$t1+y_p=\sum\limits_{i=p}^{p+k-1}x_i= k-t2-z_p$
于是我们就可以得到一大堆条件:
$\begin{cases}x_1+x_2+...+x_k=t1+y_1\\x_1+x_2+...+x_k=k-t2-z_1\\x_2+x_3+...+x_{k+1}=t1+y_2\\x_2+x_3+...+x_{k+1}=k-t2-z_2\\...\\...\\x_{n-k+1}+x_{n-k+2}+...+x_n=t1+y_{n-k+1}\\x_{n-k+1}+x_{n-k+2}+...+x_n=k-t2-z_{n-k+1}\end{cases}$
做题做的多了就很容易发现这些条件可以使用网络流来求解线性规划问题,具体可以参考 [NOI2008]志愿者招募 题解
继续推导,在条件的前后加入恒等式 $0=0$ ,然后下面的与上面的作差,并移项,即可得到:
$\begin{cases}x_1+x_2+...+x_k=(t1)+y_1\\y_1+z_1=(k-t1-t2)\\x_{k+1}+(k-t1-t2)=x_1+z_1+y_2\\y_2+z_2=(k-t1-t2)\\...\\...\\y_{n-k+1}+z_{n-k+1}=(k-t1-t2)\\(k-t2)=x_{n-k+1}+x_{n-k+2}+...+x_n=z_{n-k+1}\end{cases}$
(用括号括起来的是常数项)
这个形式中每一个变量都出现且仅出现了两次,且分别出现在等式左端与等式右端。
那么我们就可以使用网络流来解决。
把等式看作点,左端看作流出,右端看作流入(每个点的流量平衡,对应着等式的成立性);每个变量从其在左边的等式向其在右边的等式连边;对于常数项,出现在左边则想汇点连边,否则源点向其连边。
本题中变量 $x_i$ 的范围为 $[0,1]$ ,因此连的边容量为 1 ;又由于选 $x_i$ 会获得 $s_i-e_i$ 的收益,因此要带有费用 $s_i-e_i$;
$y_i$ 和 $z_i$ 的范围为 $[0,+\infty)$ ,因此连的边容量为 inf ,费用为0。
跑最大费用最大流,最大费用加上预先 “钦定” 好的 $\sum\limits_{i=1}^ne_i$ 即为最大收益。
输出方案直接看哪些边满流即可。
时间复杂度 $O(费用流)=O(能过)$
#include <queue> #include <cstdio> #include <cstring> #define N 2010 #define M 20010 #define inf 1 << 30 using namespace std; typedef long long ll; queue<int> q; int head[N] , to[M] , val[M] , id[M] , next[M] , cnt = 1 , s , t , from[N] , pre[N] , opt[N]; ll a1[N] , a2[N] , cost[M] , dis[N]; inline void add(int x , int y , int v , ll c , int i) { to[++cnt] = y , val[cnt] = v , cost[cnt] = c , id[cnt] = i , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , id[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt; } bool spfa() { int x , i; memset(from , -1 , sizeof(from)); memset(dis , 0xc0 , sizeof(dis)); dis[s] = 0 , q.push(s); while(!q.empty()) { x = q.front() , q.pop(); for(i = head[x] ; i ; i = next[i]) if(val[i] && dis[to[i]] < dis[x] + cost[i]) dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i , q.push(to[i]); } return ~from[t]; } ll maxcost() { ll ans = 0; int i , k; while(spfa()) { k = inf; for(i = t ; i != s ; i = from[i]) k = min(k , val[pre[i]]); ans += k * dis[t]; for(i = t ; i != s ; i = from[i]) val[pre[i]] -= k , val[pre[i] ^ 1] += k; } return ans; } int main() { int n , k , t1 , t2 , i , l , r; ll ans = 0; scanf("%d%d%d%d" , &n , &k , &t1 , &t2) , s = 0 , t = 2 * n - 2 * k + 4; for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &a1[i]); for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &a2[i]) , ans += a2[i]; for(i = 1 ; i <= n ; i ++ ) { if(i <= k) l = 1; else l = 2 * i - 2 * k + 1; if(i > n - k) r = 2 * n - 2 * k + 3; else r = i * 2 + 1; add(l , r , 1 , a1[i] - a2[i] , i); } for(i = 2 ; i <= 2 * n - 2 * k + 2 ; i += 2) add(i , i - 1 , inf , 0 , 0) , add(i , i + 1 , inf , 0 , 0); for(i = 2 ; i <= 2 * n - 2 * k + 2 ; i ++ ) { if(i & 1) add(i , t , k - t1 - t2 , 0 , 0); else add(s , i , k - t1 - t2 , 0 , 0); } add(s , 1 , t1 , 0 , 0) , add(2 * n - 2 * k + 3 , t , k - t2 , 0 , 0); printf("%lld\n" , ans + maxcost()); for(i = 2 ; i <= cnt ; i ++ ) if(id[i]) opt[id[i]] = val[i]; for(i = 1 ; i <= n ; i ++ ) { if(opt[i]) printf("E"); else printf("S"); } return 0; }