动态规划学习 1(最长上升子序列问题)

重新学习dp的第一步,计划学习dp用时40个学时,砥砺前行吧大伙

 

//最长上升子序列问题
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int f[N],a[N],n,res;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)
    {
        f[i]=1;
        for(int j=0;j<i;j++)
            if(a[i]>a[j])
            f[i]=max(f[i],f[j]+1);
    }
    for(int i=0;i<n;i++) res=max(res,f[i]);
    cout<<res;
    return 0;
}
// 最长上升子序列优化
// 基于贪心做法
// 首先可以证明整个f数组是单调递增的,即对于任意的索引i < j,都有 f[i] < f[j]。反证法如下:
// 若f[i] == f[j],对于f[j]对应的长度为j的最长上升子序列,其倒数第2个元素值一定小于f[j],那么这个倒数第2个元素值可以作为f[i],则有f[i] < f[j]。
// 同理可证f[i] > f[j]的情形。
// 继续分析:
// 如已扫描序列1 4 2 3,根据我们的定义,有f[3] = 3(序列1 2 3的尾元素),再往后扫描时,如果是4,即序列为1 4 2 3 4时,就可以根据f[3] = 3(3 < 4)往后延长一个LIS长度(即f长度变为4),而且使得f[4] = 4(即1 2 3 4的尾元素)。这样显然是合理的,而且可以为以后LIS长度的更新以及尾元素的更新做铺垫。
// 或者从反面考虑:如果我们f[i]存储的不是长度为i的最长上升子序列的尾元素,如f[3] = 5(存1 4 5的尾元素),那么扫描后续元素就无法最大化地延长上升序列,无法达到题目要求。
// 注意在思考问题时,从集合的角度考虑问题,即每个f[i]首先是关于所有长度为i的上升序列的函数,其次,f[i]存的是这些上升序列尾元素的最小值。
// 那么在扫描原数组的时候,对于第i个数,可以根据前i-1个数得到的f数组的结果,对f数组进行长度或内容的更新,具体表现如下:(设f数组的实际长度为cnt)
// 1、若a[i] > f[cnt],则对于从1至cnt的f中的每一个f[j],都有a[i] > f[j](因为前面已经证明f单调递增)。那么当前的a[i]可以作为长度为cnt+1的上升子序列的最小元素(因为当前只有这么一个长度为cnt+1的上升序列),即f[cnt+1] = a[i],同时cnt++.
// 2、若a[i] <= f[cnt],则无法使得上升序列变长,因为f[cnt]是长度为cnt的所有上升序列尾元素的最小值。因此,它的作用是,更新对应的一个f。显然,对于从后往前第一个小于a[i]的f[j]而言,a[i]可以接到f[j]所代表的那个最优上升序列后面,从而更新f[j+1]
// (长度为j+1的上升序列的尾元素最小值)。因为从刚才的叙述可以知道,原f[j+1] >= a[i]。故这样a[i]的作用是直接改变f的一个恰当元素值,同时为最终cnt的更新作准备(通过将一个f的元素值变小,使得后面元素接到前面更具有可能)。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,k,res,f[N],q[N],a[N];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    q[0]=-2e9;
    for(int i=0;i<n;i++)
    {
        if(q[res]<a[i]) q[++res]=a[i];
        else{
            int l=0,r=res;
            while(l<=r){
                int mid=l+r>>1;
                if(q[mid]>=a[i]) r=mid-1;
                else l=mid+1;
            }
            q[l]=a[i];
        } 
    }
    cout<<res;
    return 0;
}
//输出最长上升子序列
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,k,res,f[N],q[N],a[N],pos[N],maxx=2e9,cnt,ans[N];

int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    q[0]=-2e9,pos[0]=1;
    for(int i=0;i<n;i++)
    {
        if(q[res]<a[i]) q[++res]=a[i],pos[i]=res;
        else{
            int l=0,r=res;
            while(l<=r){
                int mid=l+r>>1;
                if(q[mid]>=a[i]) r=mid-1;
                else l=mid+1;
            }
            q[l]=a[i];
            pos[i]=l;
        } 
    }
    cnt=res;
    cout<<"最长上升子序列的长度为:"<<res<<endl;
    
    for(int i=n-1;i>=0;i--){
        if(res==0) break;
        if(pos[i]==res) ans[res]=a[i],res--;
    }
    
    cout<<"最长上升子序列为:";
    for(int i=1;i<=cnt;i++) cout<<ans[i]<<" ";
    
    return 0;
}

 

//最长上升子序列合
//f[i]是第i位的最大上升子序列合,设k为倒数第二位的话
//那f[i]=f[k]+a[i],以此类推
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,res,a[N],f[N];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)
    {
        f[i]=a[i];
        for(int j=0;j<i;j++)
        if(a[i]>a[j]) f[i]=max(f[i],f[j]+a[i]);
    }
    for(int i=0;i<n;i++) res=max(res,f[i]);
    cout<<res;
    return 0;
}
//最长不上升子序列(下降)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,a[N],q[N],res;
int main()
{
    cin>>n;
    q[0]=2e9;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)
    {
        if(q[res]>=a[i]) q[++res]=a[i];
        else{
            int l=0,r=len;
            while(l<=r){
                int mid=l+r>>1;
                if (q[mid]<a[i]) r=mid-1;
                else l=mid+1;
            }
            q[l]=a[i];
        }
    }
    cout<<res;
    return 0;
}
//怪盗基德滑翔翼
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,res,a[N],q[N],len;
int main()
{
    cin>>n;
    q[0]=-2e9;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)
    {                                                               
        if(q[len]<a[i]) q[++len]=a[i];
        else{
             int l=0,r=n-1;
             while(l<=r){
                int mid=l+r>>1;
                if(q[mid]>=a[i]) r=mid-1;
                else l=mid+1;
             }
            q[l]=a[i];
        }
    }
    res=len,len=0;
    memset(q,0,sizeof q);
    q[0]=2e9;
    for(int i=n-1;i>=0;i--)
    {
        if(q[len]<a[i]) q[++len]=a[i];
        else{
            int l=0,r=n-1;
            while(l<=r){
                int mid=l+r>>1;
                if(q[mid]>=a[i]) r=mid-1;
                else l=mid+1;
            }
        q[l]=a[i];
        }
    }
    cout<<max(res,len)<<endl;
    return 0;
}
//登山,合唱队形:https://www.luogu.com.cn/problem/P1091
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int f[N],g[N],n,m,res,a[N],t,q[N];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    for(int i=0;i<n;i++)
    {
        f[i]=1;
        for(int j=0;j<i;j++)
        if(a[i]>a[j]) f[i]=max(f[i],f[j]+1);
    }
    for(int i=n-1;i>=0;i--)
    {
        g[i]=1;
        for(int j=n-1;j>=i;j--)
        if(a[i]>a[j]) g[i]=max(g[i],g[j]+1);
    }
    for(int i=0;i<n;i++) res=max(res,g[i]+f[i]-1);
    cout<<res;//登山
    cout<<n-res;//合唱队行
    return 0;
}
//友好城市:https://www.luogu.com.cn/problem/P2782
//只需要对y从城市排序,然后求x的最长上升子序列就可以
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int x,n,res,len=0,q[N],f[N];
struct node
{
    int x,y;
    bool operator<(const node&w)const
    {
        return y<w.y;
    }
}city[N];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>city[i].x>>city[i].y;
    sort(city,city+n);
    q[0]=-2e9;
    for(int i=0;i<n;i++)
    {
        if(q[len]<city[i].x) q[++len]=city[i].x;
        else{
            int l=0,r=len;
            while(l<=r){
                int mid=l+r>>1;
                if(q[mid]>=city[i].x) r=mid-1;
                else l=mid+1;
            }
            q[l]=city[i].x;
        }
    }
    cout<<len;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,a[N],q[N],res;
int main()
{
    while(cin>>a[n]) n++;
    q[0]=2e9;
    for(int i=0;i<n;i++)
    {
        if(q[res]>=a[i]) q[++res]=a[i];
        else{
            int l=0,r=res;
            while(l<=r){
                int mid=l+r>>1;
                if(q[mid]<a[i]) r=mid-1;
                else l=mid+1;
            }
            q[l]=a[i];
        }
    }
    cout<<res<<endl;
    memset(q,0,sizeof q);
    q[0]=-2e9,res=0;
    for(int i=0;i<n;i++)
    {
        if(q[res]<a[i]) q[++res]=a[i];
        else{
            int l=0,r=res;
            while(l<=r){
                int mid=l+r>>1;
                if(q[mid]>=a[i]) r=mid-1;
                else l=mid+1;
            }
            q[l]=a[i];
        }
    }
    cout<<res<<endl;
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,a[N],res,f[N],ans;
bool vis[N];
void dfs1(int num)
{
    if(num==n){
        res++;
        return;
    }
    if(vis[num]) dfs1(num+1);
    for(int i=num+1;i<n;i++)
        if(a[i]>a[num]) vis[i]=true,dfs1(i);
}
void dfs2(int num)
{
    if(num==n){
        ans++
        return;
    }
    if(vis[num]) dfs2(num+1);
    for(int i=num+1;i<n;i++)
        if(a[i]<a[num]) vis[i]=true,dfs2(i);
}
int main()
{
    while(cin>>n&&n!=0)
    {
        res=0;
        memset(vis,false,sizeof vis);
        for(int i=0;i<n;i++) cin>>a[i];
        for(int i=0;i<n;i++) if(!vis[i]) dfs1(i);
        memset(vis,false,sizeof vis);
        for(int i=0;i<n;i++) if(!vis[i]) dfs2(i);
        res=min(res,ans);
        cout<<res<<endl;
    }
    return 0;
}
//导弹防御系统:https://www.acwing.com/problem/content/description/189/\
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],q[N],q1[N],res,n;
void dfs(int now,int up,int down)//当前的数,上升序列的个数,下降序列的个数
{
    if(up+down>=res) return;//剪枝
    if(now==n){
        res=up+down;
        return;
    }
    //情况一,上升子序列;
    //终于明白q的意义了,q里面代表的不是一个上升子序列,而是某个上升子序列的最大值
    //然后利用当前数的大小与某个上升子序列的最大值比,如果大,那就接上去,如果小,那就比下一个,如果都不行,那就新建一个
    int len=0;
    while(len<up&&a[now]<=q[len]) len++;//q序列是单调递增的,模拟一下发现,如果当前这个数无法加入当前这个上升子序列
    //也就是说,我们的q头是上升子序列的最大值,如果当前的数比它小,那么它就无法加入,而len就是新的子序列的第一个元素位置

    //这里的while循环可以用二分代替,也就是咱们那个nlogn的时间复杂度算法,所以我们这里用二分!
    //就是来找第一个<=q的元素!!


    int t=q[len];//为了回溯做准备
    q[len]=a[now];//如果能接,那就更新序列的最大值,如果不能,那就这个就是在新的序列中更新最大值 
    if(len<up) dfs(now+1,up,down);//如果len>=up,说明什么?
    //说明len的数都要比当前的数大,那就要开一个新得序列;
    //如果len<up,说明当前这个数可以加到某一个序列中去,并且更新这个序列的最大值,就不用新建序列了!!!!
    else dfs(now+1,up+1,down);//如果=它,说明这个序列需要更新了,要+1;
    q[len]=t;
    //情况二,下降子序列;
    len=0;
    while(len<down&&a[now]>=q1[len]) len++;//与上升子序列顺序相反,思路差不多;
    t=q1[len];
    q1[len]=a[now];
    if(len<down) dfs(now+1,up,down);
    else dfs(now+1,up,down+1);
    q1[len]=t;
}
int main()
{
    while(cin>>n,n){
        for(int i=0;i<n;i++) cin>>a[i];
        res=n;
        dfs(0,0,0);
        cout<<res<<endl;
    }
    return 0;
}
//最长公共上升子序列:http://poj.org/problem?id=2127
//1-1 朴素二维数组做法
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N],f[N][N],res,b[N],n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            f[i][j]=f[i-1][j];
            if(a[i]==b[j])
            {
                f[i][j]=max(f[i][j],1);
                for(int k=1;k<j;k++)
                    if(b[j]>b[k])
                    f[i][j]=max(f[i][j],f[i][k]+1);
            }
        }
    for(int i=0;i<n;i++) res=max(res,f[n][i]);
    cout<<res;
    return 0;
}
//1-2 优化算法on2
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N],f[N][N],res,b[N],n,m;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    cin>>m;
    for(int i=1;i<=m;i++) cin>>b[i];
    for(int i=1;i<=n;i++)
    {
        int maxv=1;
        for(int j=1;j<=m;j++)
        {
            f[i][j]=f[i-1][j];
            if(a[i]==b[j]) f[i][j]=max(f[i][j],maxv);
            if(a[i]>b[j]) maxv=max(maxv,f[i][j]+1);
        }
    }
    for(int i=1;i<=n;i++) res=max(res,f[n][i]);
    cout<<res;
    return 0;
}
//1-3 容易理解的优化算法
#include<bits/stdc++.h>
using namespace std;
const int N=2020;
int n,m,res,f[N][N],a[N],b[N];
int g[N][N];//g[n][n]表示的是g数组,在满足a[i]>b[j]的情况下,f[i][j]+1的最大值
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    for(int i=1;i<=n;i++)
    {
        g[i][0]=1;//从i开始,b数组的第0位设为1
        for(int j=1;j<=n;j++)//枚举b数组
        {
            f[i][j]=f[i-1][j];
            if(a[i]==b[j]) f[i][j]=max(f[i][j],g[i][j-1]);
            g[i][j]=g[i][j-1];//目前肯定是上一位的最大值嘛
            if(a[i]>b[j]) g[i][j]=max(g[i][j],f[i-1][j]+1);//如果符合,更新 
        }
    }
    for(int i=1;i<=n;i++) res=max(res,f[n][i]);
    cout<<res;
    return 0;
}

 

posted @ 2023-06-07 19:37  o-Sakurajimamai-o  阅读(32)  评论(0)    收藏  举报
-- --