[网络流24题] 最长递增子序列 (最多不相交路径---网络最大流)

731. [网络流24题] 最长递增子序列
★★★☆   输入文件:alis.in   输出文件:alis.out   简单对比
时间限制:1 s   内存限制:128 MB

«问题描述:
给定正整数序列x1,..., xn。
(1)计算其最长递增子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长
度为s的递增子序列。
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
«数据输入:
由文件alis.in提供输入数据。文件第1 行有1个正整数n(n<=500),表示给定序列的长度。接
下来的1 行有n个正整数x1,..., xn。
«结果输出:
程序运行结束时,将任务(1)(2)(3)的解答输出到文件alis.out中。第1 行是最长
递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出
的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。
输入文件示例 输出文件示例
alis.in
4
3 6 2 5

alis.out
2
2
3

算法讨论:

首先求出dp[i],表示1 ... i的最长不降子序列的长度。(本来是严格递增的,但是数据出挫了,成了不降了)
然后我们可以知道最长的不降子序列的长度 K,这是第一问的答案。
接下来把每个i拆点,拆成<i, a> <i, b>
对于每个i,
如果有dp[i] = K,那么就insert(<i,b>, T, 1)的边。
如果有dp[i] = 1, 那么就insert(S, <i, a>, 1)的边。
然后insert(<i, a>, <i, b>, 1)的边。
对于每一对 i < j,
如果 dp[j] == dp[i] + 1 && a[i] <= a[j],那么就insert(<i, b>, <j, a>, 1)的边。
跑出的最大流就是第二问的答案。
对于第三问,我们只要把第二问中对于<1> <n>的所有连边方式的容量都改成inf即可。
黄学长说这个是分层图的思想,然后边的容量就是对取的次数的限制,
由于第三问中x1 xn可以多次取,所以就把容量限制变成inf,相当于取消了次数限制。
还要一个小细节就是如果第三问中流出的流量是 > n的,就输出n。
这是因为可以无限的用x1 和 xn来构造最长的上升序列。
总感觉这个题哪里怪怪的。

代码:

#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;
const int N = 1000 + 5;
const int oo = 0x3f3f3f3f;

struct Edge {
	int from, to, cap, flow;
	Edge(int u = 0, int v = 0, int c = 0, int f = 0) :
		from(u), to(v), cap(c), flow(f) {}
};

struct Dinic {
	int nn, mm, s, t;
	int dis[N], cur[N], que[N * 10];
	bool vis[N];
	vector <Edge> edges;
	vector <int> G[N];

	void clear() {
		for(int i = 0; i <= nn; ++ i) G[i].clear();
		edges.clear();
	}
	void add(int from, int to, int cap) {
		edges.push_back(Edge(from, to, cap, 0));
		edges.push_back(Edge(to, from, 0, 0));
		mm = edges.size();
		G[from].push_back(mm - 2);
		G[to].push_back(mm - 1);
	}
	bool bfs() {
		int head = 1, tail = 1;
		memset(vis, false, sizeof vis);
		dis[s] = 0; que[head] = s; vis[s] = true;
		while(head <= tail) {
			int x = que[head];
			for(int i = 0; i < (signed) G[x].size(); ++ i) {
				Edge &e = edges[G[x][i]];
				if(!vis[e.to] && e.cap > e.flow) {
					dis[e.to] = dis[x] + 1;
					vis[e.to] = true;
					que[++ tail] = e.to;
				}
			}
			++ head;
		}
		return vis[t];
	}
	int dfs(int x, int a) {
		if(x == t || a == 0) return a;
		int flw = 0, f;
		for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) {
			Edge &e = edges[G[x][i]];
			if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) {
				e.flow += f; edges[G[x][i] ^ 1].flow -= f;
				a -= f; flw += f;
				if(!a) break;
			}
		}
		return flw;
	}
	int maxflow(int s, int t) {
		this->s = s; this->t = t;
		int flw = 0;
		while(bfs()) {
			memset(cur, 0, sizeof cur);
			flw += dfs(s, oo);
		}
		return flw;
	}
}net;

int n, S, T;
int a[N], dp[N];

#define stone_

int main() {
#ifndef stone_
	freopen("alis.in", "r", stdin);
	freopen("alis.out", "w", stdout);
#endif

	scanf("%d", &n);
	S = 0; T = (n << 1) | 1; net.nn = T;
	for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
	dp[n] = 1;
	for(int i = 1; i <= n; ++ i) {
		dp[i] = 1;
		for(int j = 1; j < i; ++ j)
		  if(a[i] >= a[j])
		    dp[i] = max(dp[i], dp[j] + 1);
	}
	int result = 0;
	for(int i = 1; i <= n; ++ i) result = max(result, dp[i]);
	for(int i = 1; i <= n; ++ i) {
		if(dp[i] == result) net.add(i + n, T, 1);
		if(dp[i] == 1) net.add(S, i, 1);
		net.add(i, i + n, 1);
	}
	for(int i = 1; i <= n ; ++ i)
	  for(int j = i + 1; j <= n; ++ j)
			if(j != i) if(dp[j] == dp[i] + 1 && a[i] <= a[j])
				  net.add(i + n, j, 1);
	printf("%d\n%d\n", result, net.maxflow(S, T));
	net.clear();
	for(int i = 1; i <= n; ++ i) {
		int v = 1;
		if(i == 1 || i == n) v = oo;
		if(dp[i] == result) net.add(i + n, T, v);
		if(dp[i] == 1) net.add(S, i, v);
		net.add(i, i + n, v);
	}
	for(int i = 1; i <= n ; ++ i)
	  for(int j = i + 1; j <= n; ++ j)
			if(j != i) if(dp[j] == dp[i] + 1 && a[i] <= a[j])
				  net.add(i + n, j, 1);
	int ans = net.maxflow(S, T);
	if(ans > n) printf("%d\n", n);
	else printf("%d\n", ans);

#ifndef stone_
	fclose(stdin); fclose(stdout);
#endif
	return 0;
}
时间: 2024-08-08 05:38:43

[网络流24题] 最长递增子序列 (最多不相交路径---网络最大流)的相关文章

[网络流24题] 最长递增子序列

[网络流24题] 最长递增子序列 «问题描述:给定正整数序列x1,..., xn.(1)计算其最长递增子序列的长度s.(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列.(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列. 注意:这里的最长递增子序列即最长不下降子序列!!!«编程任务:设计有效算法完成(1)(2)(3)提出的计算任务.«数据输入:由文件alis.in提供输入数据.文件第1 行有1个正整数n(n<=500),表示给定序列的长

[网络流24题]最长递增子序列问题

题目大意:给定长度为n的序列a,求:1.最长递增子序列长度:2.最多选出几个不相交的最长递增子序列:3.最多选出几种在除了第1个和第n个以外的地方不相交的最长递增子序列.(n<=1000) 思路:先倒着DP,求出f[i]表示以a[i]开头的最长的递增子序列长度,然后建图,若f[i]=最长递增子序列长度则S向i连1,若f[i]=1则i向T连1,若i<j且a[i]<a[j]且f[i]=f[j]+1则i向j连1,为保证每个点只被流一次,拆成入点和出点,流量限制1,跑最大流即可解决第二问,点1和

[网络流24题] 太空飞行计划 (最大权闭合子图---网络最大流)

727. [网络流24题] 太空飞行计划 ★★☆ 输入文件:shuttle.in 输出文件:shuttle.out 简单对比 时间限制:1 s 内存限制:128 MB [问题描述] W 教授正在为国家航天中心计划一系列的太空飞行.每次太空飞行可进行一系列商业性实验而获取利润.现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={ I1, I2,…,In }.实验Ej 需要用到的仪器是I的子集Rj∈I.配置仪器Ik 的费用为ck 美元.实验Ej

[网络流 24 题]最长k可重区间集(费用流)

Description 给定实直线L 上n 个开区间组成的集合I,和一个正整数k,试设计一个算法,从开区间集合I 中选取出开区间集合S属于I,使得在实直线L 的任何一点x,S 中包含点x 的开区间个数不超过k,且sum(|z|)z属于S,达到最大.这样的集合S称为开区间集合I的最长k可重区间集.sum(|z|) z属于S称为最长k可重区间集的长度.对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度. Solution 1.离散化 然后从每个点i向i+1连一条流量为INF,

LiberOJ 6003. 「网络流 24 题」魔术球 贪心或者最小路径覆盖

6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 假设有 n nn 根柱子,现要按下述规则在这 n nn 根柱子中依次放入编号为 1,2,3,4,? 1, 2, 3, 4, \cdots1,2,3,4,? 的球. 每次只能在某根柱子的最上面放球. 在同一根柱子中,任何 2 22 个相邻球的编号之和为完全平方数. 试设计一个算法,计算出在 

【网络流24题】分配问题(二分图最佳匹配)(费用流)

[网络流24题]分配问题 2014年3月11日1,8720 题目描述 Description 有n件工作要分配给n个人做.第i 个人做第j 件工作产生的效益为ij c .试设计一个将n件工作分配给n个人做的分配方案,使产生的总效益最大.«编程任务:对于给定的n件工作和n个人,计算最优分配方案和最差分配方案. 输入描述 Input Description 第1 行有1 个正整数n,表示有n件工作要分配给n 个人做.接下来的n 行中,每行有n 个整数 cij ,1≤i≤n,1≤j≤n,表示第i 个人

【网络流24题】No. 13 星际转移问题 (网络判定 最大流)

[题意] 由于人类对自然资源的消耗, 人们意识到大约在 2300 年之后, 地球就不能再居住了.于是在月球上建立了新的绿地,以便在需要时移民. 令人意想不到的是, 2177 年冬由于未知的原因, 地球环境发生了连锁崩溃, 人类必须在最短的时间内迁往月球. 现有 n 个太空站位于地球与月球之间,且有 m 艘公共交通太空船在其间来回穿梭.每个太空站可容纳无限多的人, 而每艘太空船 i 只可容纳 H[i]个人.每艘太空船将周期性地停靠一系列的太空站,例如: (1, 3, 4)表示该太空船将周期性地停靠

洛谷2766:[网络流24题]最长不下降子序列问题——题解

https://www.luogu.org/problemnew/show/P2766 给定正整数序列x1,...,xn . (1)计算其最长不下降子序列的长度s. (2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列. (3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列. 第一问用dp求解不多说了. 考虑第二问,每个数只用一次很好办,把数拆点(入点和出点)中间连边权为1的边即可. 现在的问题就是如何让它能够跑满s个点. 我们设dp[i

P2766 [网络流24题]最长不下降子序列问题

ha~ ?问题描述: 给定正整数序列$x_1,...,x_n$ .$n<=500$ 求(1)计算其最长不下降子序列的长度$s$. (2)计算从给定的序列中最多可取出多少个长度为$s$的不下降子序列. (3)如果允许在取出的序列中多次使用$x_1$和$x_n$,则从给定序列中最多可取出多少个长度为$s$的不下降子序列. (1)暴力n方解决 (2)建分层图,把图每个顶点i按照F[i]的不同分为若干层,这样图中从S出发到T的任何一条路径都是一个满足条件的最长不下降子序列.由 S 向所有$ f_i =