1. 程式人生 > >@BZOJ - [email protected] 序列

@BZOJ - [email protected] 序列

目錄


@[email protected]

給出一個長度為 N 的正整數序列 Ci,求一個子序列(顯然子序列是不一定連續的),使得原序列中任意長度為 M 的子串(顯然子串是連續的)中被選出的元素不超過 K 個,並且選出的元素之和最大。

input
第一行三個整數:N M K。
第二行 N 個整數,第 i 個為 Ci。

output
輸出一個整數:最大和。

sample input
10 5 3
4 4 4 6 6 6 6 6 4 4
sample output
30

hint
N <= 1000,K,M <= 100, Ci <= 20000。

P. S.:原題題目描述非常迷,大小寫混用,有些地方缺少字母,input 的部分還出現了 “接下來 N 行” 這種無法理解的操作。

@[email protected]

考慮弱化條件,當 K = 1 時,相當於選擇的元素兩兩之間必須要相隔 M 的距離,可以用 dp 來搞定。
當 K > 1 時,就不能這麼搞了。

類比網路流 24 題中的 “最長k可重區間問題”,那道題也是當 K = 1 時可以用 dp 來做。
當 K > 1 時,那道題建模的方法就是由源點流出 K 個流,i 向 i+1 連容量 inf,費用為 0 的 “無效邊”,N 再向匯點連邊。中間連一些容量為 1,費用為權值的邊。
這樣建模,如果進入 i 的流全部流過 (i, i+1) 這條邊,相當於不選擇 i 。當 K = 1 時就是一個最短路,自然可以用 dp 來做。

對於這道題,我們可以採用類似的建模方法:
源點向 1 連容量為 K,費用為 0 的邊;N 向匯點連容量為 K,費用為 0 的邊。
i 向 i+1 連容量為 inf,費用為 0 的 “無效邊”。
如果 i+M <= N,i 向 i+M 連容量為 1,費用為 Ci 的邊。
如果 i+M > N,i 向匯點連容量為 1,費用為 Ci 的邊。

【感性理解】:假如有 p 個流進入了 i。全部流 (i, i+1) 這條邊相當於不選擇 i;假如流 (i, i+M) 這條邊,則 i+1 ~ i+M-1 這些點能得到的最多的流為 p-1,i+M 之後恢復為 p,而 i 所能影響的最遠也只能到 i+M 之前。所以得到的流是符合題意的。

注意是最大費用流,權值取反。

@accepted [email protected]

不知道為什麼跑得就是比其他人慢……我是自帶常數嗎 QAQ?

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int INF = (1<<30);
const int MAXN = 1000;
const int MAXV = 1000;
const int MAXE = 5000;
struct FlowGraph{
    struct edge{
        int to, flow, cap, dis;
        edge *nxt, *rev;
    }edges[2*MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
    int S, T, cost, mindis, dist[MAXN + 5];
    bool inq[MAXV + 5], vis[MAXV + 5];
    deque<int>que;
    void init(int _S, int _T) {
        S = _S, T = _T;
        ecnt = &edges[0];
        for(int i=S;i<=T;i++)
            adj[i] = NULL;
    }
    void addedge(int u, int v, int c, int w) {
        edge *p = (++ecnt);
        p->to = v, p->cap = c, p->dis = w, p->flow = 0;
        p->nxt = adj[u], adj[u] = p;
        edge *q = (++ecnt);
        q->to = u, q->cap = 0, q->dis = -w, q->flow = 0;
        q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
    }
    void restore() {
        for(int i=S;i<=T;i++)
            dist[i] = INF, cur[i] = adj[i];
    }
    bool relabel() {
        que.push_back(S); inq[S] = true; dist[S] = 0;
        while( !que.empty() ) {
            int f = que.front(); que.pop_front(); inq[f] = false;
            for(edge *p=adj[f];p!=NULL;p=p->nxt) {
                if( p->cap > p->flow && dist[f] + p->dis < dist[p->to] ) {
                    dist[p->to] = dist[f] + p->dis;
                    if( !inq[p->to] ) {
                        if( !que.empty() && dist[p->to] < dist[que.front()] )
                            que.push_front(p->to);
                        else que.push_back(p->to);
                        inq[p->to] = true;
                    }
                }
            }
        }
        mindis = dist[T];
        return !(dist[T] == INF);
    }
    int aug(int x, int tot) {
        if( x == T ) {
            cost += tot*mindis;
            return tot;
        }
        int sum = 0; vis[x] = true;
        for(edge *&p=cur[x];p!=NULL;p=p->nxt) {
            if( !vis[p->to] && p->cap > p->flow && dist[x] + p->dis == dist[p->to] ) {
                int del = aug(p->to, min(p->cap-p->flow, tot-sum));
                sum += del, p->flow += del, p->rev->flow -= del;
                if( sum == tot ) break;
            }
        }
        vis[x] = false;
        return sum;
    }
    int min_cost_max_flow() {
        int flow = 0; restore();
        while( relabel() )
            flow += aug(S, INF), restore();
        return flow;
    }
}G;
int C[MAXN + 5];
int main() {
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    for(int i=1;i<=n;i++)
        scanf("%d", &C[i]);
    G.init(0, n+1);
    G.addedge(G.S, 1, k, 0);
    for(int i=1;i<=n-m;i++)
        G.addedge(i, i+m, 1, -C[i]);
    for(int i=n-m+1;i<=n;i++)
        G.addedge(i, G.T, 1, -C[i]);
    for(int i=1;i<=n;i++)
        G.addedge(i, i+1, INF, 0);
    G.addedge(n, G.T, k, 0);
    int ans = G.min_cost_max_flow();
    printf("%d\n", -G.cost);
}

@[email protected]

事實證明,做網路流 24 題還是有那麼一點用的。
除了那道毒瘤機器人路徑規劃。
弱化條件下可以用 dp,可以把 dp 看作最短路,即最大流為 1 時的最小費用流。這樣就可以進行相應的網路流建模。