【bzoj1283】序列 線性規劃與費用流
題目描述
給出一個長度為 的正整數序列Ci,求一個子序列,使得原序列中任意長度為 的子串中被選出的元素不超過K(K,M<=100) 個,並且選出的元素之和最大。
輸入
第1行三個數N,m,k。 接下來N行,每行一個字符串表示Ci。
輸出
最大和。
樣例輸入
10 5 3
4 4 4 6 6 6 6 6 4 4
樣例輸出
30
題解
線性規劃與費用流
關於線性規劃與費用流的具體講解參見 bzoj1061 。
這道題和那道差不多,都是給出一大堆限制條件,每個變量在限制條件中的出現是連續的。
所以我們可以按照那道題的思路來做。
原始限制條件是$\begin{cases}0\le x_i\le1\\x_1+x_2+...+x_m\le k\\x_2+x_3+...+x_{m+1}\le k\\...\\x_{n-m+1}+x_{n-m+2}+...+x_n\le k\end{cases}$,
轉化為相等關系為$\begin{cases}0\le x_i\le1\\y_i\ge0\\x_1+x_2+...+x_m+y_1\le k\\x_2+x_3+...+x_{m+1}+y_2\le k\\...\\x_{n-m+1}+x_{n-m+2}+...+x_n+y_{n-m+1}\le k\end{cases}$,
添加恒等關系0=0,上下差分並移項得$\begin{cases}x_1+x_2+...+x_m+y_1-k=0\\x_{m+1}-x_1+y_2-y_1=0\\x_{m+2}-x_2+y_3-y_2=0\\...\\x_{n-1}-x_{n-m-1}+y_{n-m}-y_{n-m-1}=0\\x_n-x_{n-m}+y_{n-m+1}-y_{n-m}=0\\-x_{n-m+1}-x_{n-m+2}-...-x_n-y_{n-m+1}+k=0\end{cases}$。
根據這個建圖,將這n-m+2個限制條件看作點,那麽S->1,容量為k,費用為0;n-m+2->T,容量為k,費用為0;i->i+1,容量為inf,費用為0;對於每個變量xi,判斷它系數為+1的位置和系數為-1的位置,+1向-1連邊。容量為1,費用為ci。
然後跑最大費用最大流出解,具體地,將費用取相反數,跑最小費用最大流,再反過來即可。
#include <cstdio> #include <cstring> #include <queue> #define N 1500 #define M 30000 #define inf 0x3f3f3f3f using namespace std; queue<int> q; int head[N] , to[M] , val[M] , cost[M] , next[M] , cnt = 1 , s , t , dis[N] , from[N] , pre[N]; void add(int x , int y , int v , int c) { to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt; } bool spfa() { int x , i; memset(from , -1 , sizeof(from)); memset(dis , 0x3f , 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]; } int mincost() { int ans = 0 , 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 , m , k , i , x; scanf("%d%d%d" , &n , &m , &k) , s = 0 , t = n - m + 3; add(s , 1 , k , 0) , add(n - m + 2 , t , k , 0); for(i = 1 ; i <= n - m + 1 ; i ++ ) add(i , i + 1 , inf , 0); for(i = 1 ; i <= n ; i ++ ) { scanf("%d" , &x); if(i <= m) add(1 , i + 1 , 1 , -x); else if(i > n - m) add(i - m + 1 , n - m + 2 , 1 , -x); else add(i - m + 1 , i + 1 , 1 , -x); } printf("%d\n" , -mincost()); return 0; }
【bzoj1283】序列 線性規劃與費用流