最长递增子序列问题[网络流24题之6]
2016-05-21 14:13
351 查看
问题描述:
给定正整数序列 x1,x2,...,xn 。(1) 计算其最长递增子序列的长度 s 。
(2) 计算从给定的序列中最多可取出多少个长度为 s 的递增子序列。
(3) 如果允许在取出的序列中多次使用 x1 和 xn,则从给定序列中最多可取出多少个长度为 s 的递增子序列。
编程任务:
设计有效算法完成 (1)(2)(3) 提出的计算任务。数据输入:
第 1 行有 1 个正整数 n ,表示给定序列的长度。接下来的 1 行有 n 个正整数x1,x2,...,xn结果输出:
输出共 3 行第 1 行是最长递增子序列的长度 s 。
第 2 行是可取出的长度为 s 的递增子序列个数。
第 3 行是允许在取出的序列中多次使用 x1和 xn 时可取出的长度为 s 的递增子序列个数。
输入文件示例:
43 6 2 5
输出文件示例:
22
3
分析:
第 (1) 问是个经典的 DP 问题,因为数据范围不大,直接 n2的算法就可以了,不需要 nlogn的算法优化第 (2) 问的求序列的个数,构建图 N 时就可以用到 n2 算法中求得的 cnt 数组 ( 逆序求 cnt 数组 ),每一次加边操作是,对于两个数 (ax,ay) 当且仅当 ax<=ay 并且 cntx=cnty+1 (这样就保证了每次加边的两个顶点的先后关系,不会出现求最大流时有路径的长度小于第 1 问的 ans )时将 (x,y) 加入图中,具体过程:
1 :新增源 S 和汇 T
2 :若 cntx=1 ,则在 S 和 x 中连一条容量为 1 的有向边
3 :若 cnty=ans(第一问) ,则在 x 与 T 之间连一条容量为 1 的有向边
4 :若 ax<=ay且cntx=cnty+1 ,则在 x 与 y 之间连一条容量为 1 的有向边
第 (3) 问则只需要让边 (S,1) 和 (n,T) 的容量改为 ∞即可
代码:
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int inf = 0x3f3f3f3f; int cnt[547],ans; int cur[547]; int head[547],nxt[4747],to[4747],wei[4747],tot=1; int c[547]; int que[547]; int n; int a[547]; int read(); void add(int,int,int); bool bfs(); bool dinic(int); int get_cnt(); int main(){ n = read(); for(int i=1;i<=n;++i) a[i] = read(); ans = get_cnt(); printf("%d\n",ans); for(int i=n;i>=1;--i){ if(cnt[i] == 1) add(i,546,1); if(cnt[i] == ans) add(545,i,1); for(int j=i+1;j<=n;++j) if(cnt[i]==cnt[j]+1 && a[j]>=a[i]) add(i,j,1); } int num=0; while(bfs()){ memcpy(cur,head,sizeof head); while(dinic(545)) ++num; } printf("%d\n",num); for(int i=2;i<tot;i+=2){ wei[i] = 1; wei[i^1] = 0; } for(int i=head[545];i;i=nxt[i]) if(to[i] == 1){ wei[i] = inf; break; } for(int i=head ;i;i=nxt[i]) if(to[i] == 546){ wei[i] = inf; break; } num = 0; while(bfs()){ memcpy(cur,head,sizeof head); while(dinic(545)) num++; } printf("%d",num); return 0; } int read(){ int in = 0; char ch = getchar(); while(ch>'9'||ch<'0') ch = getchar(); while(ch<='9'&&ch>='0'){ in = in*10+ch-'0'; ch = getchar(); } return in; } void add(int from,int tp,int value){ ++tot;nxt[tot]=head[from];head[from]=tot;to[tot]=tp;wei[tot]=value; ++tot;nxt[tot]=head[tp];head[tp]=tot;to[tot]=from;wei[tot]=0; } int get_cnt(){ int m = 1; for(int i=n;i>=1;--i){ cnt[i] = 1; for(int j=i+1;j<=n;++j) if(a[i]<=a[j] && cnt[j]>=cnt[i]) cnt[i] = cnt[j]+1; } for(int i=1;i<=n;++i) if(cnt[i] > m) m = cnt[i]; return m; } bool bfs(){ int now,H=0,T=1; memset(c,0,sizeof c); c[545] = 1; que[1] = 545; do{ now = que[++H]; for(int i=head[now];i;i=nxt[i]) if(!c[to[i]] && wei[i]){ c[to[i]] = c[now]+1; que[++T] = to[i]; } }while(H<T); return c[546]; } bool dinic(int place){ if(place == 546) return true; for(int i=head[place];i;i=nxt[i]) if(c[to[i]]==c[place]+1 && wei[i]) if(dinic(to[i])){ --wei[i]; ++wei[i^1]; return true; } return false; }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性