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

网络流与线性规划24题06最长递增子序列问题

2013-03-28 20:46 295 查看
问题描述:

给定正整数序列X1....... Xn 。

(1)计算其最长递增子序列的长度s。

(2)计算从给定的序列中最多可取出多少个长度为s的递增子序列。

(3)如果允许在取出的序列中多次使用X1和Xn,则从给定序列中最多可取出多少个长

度为s的递增子序列。

编程任务:

设计有效算法完成(1)(2)(3)提出的计算任务。

数据输入:

由文件input.txt提供输入数据。文件第1 行有1个正整数n,表示给定序列的长度。接

下来的1 行有n个正整数X1......Xn 。

结果输出:

程序运行结束时,将任务(1)(2)(3)的解答输出到文件output.txt中。第1 行是最长

递增子序列的长度s。第2行是可取出的长度为s 的递增子序列个数。第3行是允许在取出

的序列中多次使用x1和xn时可取出的长度为s 的递增子序列个数。

输入示例:

4

3 6 2 5

输出示例

2

2

3

分析:

首先要说的是示例数据第三个不应该是3,先放着,到后面在谈。首先第一问是经典的LIS问题,即最长递增子序列,用DP很容易就解出来了,f(i)表示以i结尾的序列的最长递增子序列长度。状态转移方程是:f(i)=max{ f(j) | j<i,a[j]<a[i]}+1。这是一个O(n^2)的算法,有更快的O(nlogn),不过我没写,因为数据规模很小,O(n^2)够了。以后准备写一个整理算法的专栏,到时候会介绍。

然后,建立最大流模型,这个模型就是有节点容量的网络流模型,把每个点拆成入点和出点,连接出入点,边权为1表示每个点只能选择一次。创建源点和汇点,连接源点和每个f[i]=1的点,连接汇点和每个f[i]=s的点,然后对于每个两个点,如果f[i]+1=f[j],则连接i点和j点,边权为正无穷。然后求最大流就可。

第三问我感觉有点小漏洞,不知道是我想错了还是什么,就是把X1,Xn出入点边权正无穷,然后把源点和X1入点正无穷,如果f
=s,就把Xn出点和汇点连接边权正无穷,然后再求一次最大流。

可是这样有一个bug,如果f
=2,就会产生错误,因为从源点->X1->Xn->汇点边权都是正无穷。所以感觉题目出的很怪异,不过暂且就这么解吧,可能是我目前境界不够。所以如果复制了这个代码,运行出错,千万别怪我。

或者有哪位大牛能指出我的BUG,感激不尽。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 660;
const int INF = 1<<30;
struct edge{
int from,to,cap,flow;
edge(int a,int b,int c,int d):from(a),to(b),cap(c),flow(d)
{}
};
vector<int> g[maxn];
vector<edge> edges;
int a[maxn];
int f[maxn];
bool visit[maxn];
int d[maxn];
int cur[maxn];
int t,s;
void addedge(int from,int to,int cap)
{
edges.push_back(edge(from,to,cap,0));
edges.push_back(edge(to,from,0,0));
int m=edges.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
void buildgraph(int n,int m)
{
s=0;t=2*n+1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++)
if(f[i]+1==f[j]) addedge(i+n,j,INF);//连接的边权为无穷
addedge(i,i+n,1);//拆点为2个,权值为1,保证每个点只选一次
if(f[i]==1) addedge(s,i,1);
if(f[i]==m) addedge(i+n,t,1);
}
}
bool BFS()
{
memset(visit,false,sizeof(visit));
d[s]=0;
queue<int> q;
q.push(s);
visit[s]=true;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(!visit[e.to]&&e.cap>e.flow){
visit[e.to]=true;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return visit[t];
}
int DFS(int x,int a)
{
if(x==t||a==0) return a;
int flow=0,f;
for(int &i=cur[x];i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(d[e.to]==d[x]+1&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){
flow+=f;
a-=f;
e.flow+=f;
edges[g[x][i]^1].flow-=f;
if(a==0) break;
}
}
return flow;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
//第一问,DP求LIS
int max=0;
f[1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++)
if(a[i]>a[j]&&f[j]>f[i]) f[i]=f[j];
f[i]++;
if(f[i]>max) max=f[i];
}
printf("%d\n",max);
//第二问
buildgraph(n,max);
int maxflow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
maxflow+=DFS(s,INF);
}
printf("%d\n",maxflow);
//第三问
addedge(1,1+n,INF);
addedge(n,n+n,INF);
addedge(s,1,INF);
if(f
==max) addedge(n+n,t,INF);
while(BFS()){
memset(cur,0,sizeof(cur));
maxflow+=DFS(s,INF);
}
printf("%d\n",maxflow);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: