您的位置:首页 > 理论基础 > 计算机网络

最长递增子序列问题[网络流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 的递增子序列个数。

输入文件示例:

4

3 6 2 5

输出文件示例:

2

2

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++