【P2766】 最长不下降子序列问题
题目描述
«问题描述:
给定正整数序列x1,...,xn 。
(1)计算其最长不下降子序列的长度s。
(2)计算从给定的序列中最多可取出多少个长度为s的不下降子序列。
(3)如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的不下降子序列。
«编程任务:
设计有效算法完成(1)(2)(3)提出的计算任务。
输入格式
第1 行有1个正整数n,表示给定序列的长度。接下来的1 行有n个正整数n:x1, ..., xn。
输出格式
第1 行是最长不下降子序列的长度s。第2行是可取出的长度为s 的不下降子序列个数。第3行是允许在取出的序列中多次使用x1和xn时可取出的长度为s 的不下降子序列个数。
输入输出样例
输入 #1
4
3 6 2 5
输出 #1
2
2 3
说明/提示
n≤500
【解题思路】
运用最小割解决问题的本质是
找到多个问题同时存在且矛盾的情况,利用网络流找到最优的解
于是这个问题的解决方式就能够这样解决
首先,拆点,一个入点一个出点,考虑他们的连接过程
因为源头流量为 1 ,于是就能够形成单一的链,保证情况独立,能够排除矛盾情况
开头的是长度为 1 的,因为最长的能够包括所有子串,所以连接至汇点
然后对每个符合要求,对这道题来说就是
能够顺利组成增加一个字符串,使这个子串增长
如果选择了这个子串之后,就能够或者这个子串相应的流量
在最大流的性质和思想下最终也就能够得到最终答案了
【代码】
#include<cstdio> #include<queue> #include<cstring> #define ll int using namespace std; const int MAXN = 5010; const int MAXM = 200010; const ll INF = (1ll << 31) - 1; struct note { int to; int nt; int rev; ll cal; }; struct edge { note arr[MAXM]; int siz; int maxn; int a[MAXN]; int dp[MAXN]; int st[MAXN]; int dis[MAXN]; int cur[MAXN]; int depth[MAXN]; int top; int n, m, s, t; edge() { memset(st, -1, sizeof(st)); memset(depth, -1, sizeof(depth)); memset(dis, -1, sizeof(dis)); top = 0; } void read() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); } int LIS() { for (int i = 1; i <= n; i++) dp[i] = 1; for (int i = 1; i <= n; i++) for (int j = 1; j < i; j++) { if (a[j] <= a[i]) { dp[i] = max(dp[i], dp[j] + 1); } } maxn = -1; for (int i = 1; i <= n; i++) if (maxn < dp[i]) maxn = dp[i]; return maxn; } void build() { t = 2*n+2; for (int i = 1; i <= n; i++) { add(i, i + n, 1); if(dp[i]==1) add(0, i, 1); if (dp[i] == maxn) add(i + n, t, 1); } for (int i = 1; i <= n; i++) for (int j = 1; j <i; j++) if (a[j] <= a[i] && dp[j] + 1 == dp[i]) add(j+n, i , 1); s = 0; siz = 2 * n+1; } bool dep() { queue<int> q; q.push(s); memset(depth, -1, sizeof(depth)); depth[s] = 0; while (!q.empty()) { int v = q.front(); q.pop(); for (int i = st[v]; i != -1; i = arr[i].nt) { int to = arr[i].to; if (!arr[i].cal) continue; if (depth[to] != -1) continue; depth[to] = depth[v] + 1; q.push(to); } } return (depth[t] != -1); } void add(int x, int y, ll z) { top++; arr[top] = { y,st[x],top + 1,z }; st[x] = top; top++; arr[top] = { x,st[y],top - 1,0 }; st[y] = top; } ll dfs(int now, ll val) { if (now == t || !val) return val; ll flow = 0; for (int& i = cur[now]; i != -1; i = arr[i].nt) { int to = arr[i].to; if (depth[to] != depth[now] + 1) continue; ll f = dfs(to, min(arr[i].cal, val)); if (!f || !arr[i].cal) continue; flow += f; arr[i].cal -= f; arr[arr[i].rev].cal += f; val -= f; if (!val) return flow; } return flow; } ll dinic() { ll flow = 0; ll f; while (dep()) { for (int i = 0; i <= siz; i++) cur[i] = st[i]; while (f = dfs(s, INF)) flow += f; } return flow; } }; edge road,tmp; int main() { road.read(); //printf("**\n"); printf("%d\n",road.LIS()); //printf("**\n"); //printf("\n"); road.build(); //printf("**\n"); tmp = road; printf("%d\n", tmp.dinic()); if(road.dp[1]==1) road.add(road.s, 1, INF); if(road.dp[road.n]==road.maxn) road.add(road.n * 2, road.t, INF); road.add(1, road.n + 1, INF); road.add(road.n, road.n * 2, INF); printf("%d", road.dinic()); return 0; }