網絡流與線性規劃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;
}



發佈了34 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章