zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    给出一个长度为 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 行” 这种无法理解的操作。

    @solution@

    考虑弱化条件,当 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 code@

    不知道为什么跑得就是比其他人慢……我是自带常数吗 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
    ", -G.cost);
    }
    

    @details@

    事实证明,做网络流 24 题还是有那么一点用的。
    除了那道毒瘤机器人路径规划。
    弱化条件下可以用 dp,可以把 dp 看作最短路,即最大流为 1 时的最小费用流。这样就可以进行相应的网络流建模。

  • 相关阅读:
    linux操作系统及内核
    2.1.1Remove Duplicates from Sorted Arr
    顺序表
    开博篇
    ssh无法root用户登录与登录界面无法选择用户登录
    Ubuntu 18.04 Server安装GUI桌面
    Linux 命令 su 和 sudo 区别
    坑(二十六)—— mysql出现ERROR1698(28000):Access denied for user root@localhost错误解决方法
    redhat安装wps
    gitlab重置root密码
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10177308.html
Copyright © 2011-2022 走看看