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

问题描述:
给定正整数序列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[n]=s,就把Xn出点和汇点连接边权正无穷,然后再求一次最大流。

可是这样有一个bug,如果f[n]=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[n]==max) addedge(n+n,t,INF);
    while(BFS()){
        memset(cur,0,sizeof(cur));
        maxflow+=DFS(s,INF);
    }
    printf("%d\n",maxflow);
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值