线性dp acwing基础课 提高课打卡

线性dp 基础课+提高课

1

    友好城市1012. 友好城市 - AcWing题库

    

 题意:给定有序对 两个数分别在两根数轴上,问如果将有序对连线,最多可以连几条(保证任意一个点至多有唯一的数与之对应)

思维 +LIS

按照任意一维数据排序之后做LIS即可保证题目的要求

代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
const int N=5010;
int n;
int f[N];
struct Node{
    int a,b;
    bool operator<(const Node&s)const
    {
        return a<s.a;
    }
}node[N];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d%d",&node[i].a,&node[i].b);
    
    sort(node+1,node+n+1);
    int res=0;
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        for(int j=1;j<i;j++)
          if(node[j].b<node[i].b)f[i]=max(f[i],f[j]+1);
        res=max(res,f[i]);
    }
    
    
    cout<<res;

}

2

LIC2模板

二分优化暴力的LIC1

(这道题可以和单调队列对比一下~)

3 1  4  5  6

如上面的例子 能接在3后面的一定能接在1后面,且1的阈值更大

维护数组q,q[i]为长度为i的上升子序列下的最小的一个a(原数组的值)

可以看出这样的q是一个单调的 每次从q中找出小于a[i]的最大的数字 接在它的后面是最优的,然后更新一下q数组即可

代码如下:

#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],q[N];

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int len=0;
    
    int l,r;
    
    for(int i=1;i<=n;i++)
    {
        l=0,r=len;
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(q[mid]>=a[i])r=mid-1;
            else l=mid;
        }
        len=max(len,l+1);
        q[l+1]=a[i];
      
    }
    
    cout<<len;
}

3

LIS 模板

这个和一般的集合的划分后计算不太一样,是一种萌新的我不能自己推出的 是看了之后了解的这种计算的方式

 

代码如下:

#include<iostream>
using namespace std;
const int N=1010;
int f[N][N];
int n,m;
char a[N],b[N];
int main()
{
    cin>>n>>m;
    scanf("%s",a+1);
    scanf("%s",b+1);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      {
          f[i][j]=max(f[i][j-1],f[i-1][j]);
          if(a[i]==b[j])f[i][j]=max(f[i][j],f[i-1][j-1]+1);
      }
      
      
    cout<<f[n][m];
}

4

 拦截导弹1010. 拦截导弹 - AcWing题库

这道题的贪心的证明思路对我这种小萌新比较有启发,所以写一下

贪心的思路:

  从头到尾扫,当前元素接到能接上的最小的后面 (这样对阈值的”使用“最小)

 如果都不能接上的话 我们就开一个新的位置 

证明如下:

即如何证明贪心解是我们的最优解

①首先贪心解一定是我们的一个合法解

②从最优解中找到我们第一个和贪心解不一样的地方,

-----a-x-----贪心解

-----b-x-----最优解

a<=b 故可以将最优解调整为贪心解 所以我们的贪心思路正确

(这里给出了证明两个集合相等的方法 也是最常用的一个方法)

另外这里有不限定元素的读取方法

还有一种是流读入如下图所示:

  string line;
   getline(cin,line);
   stringstream ssin(line);
   while(ssin>>a[n])n++;

代码如下:

#include<iostream>
using namespace std;
const int N=1e5+10;
int f[N];
int a[N];
int g[N];
int n=1;
int main()
{
    while(cin>>a[n])n++;
    n--;
    int res=1;
    for(int i=1;i<=n;i++)
    {
        f[i]=1;
        for(int j=1;j<i;j++)
         if(a[j]>=a[i])f[i]=max(f[i],f[j]+1);
         
        res=max(res,f[i]);
    }
    
    cout<<res<<endl;
    
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        int k=1;
        while(k<=cnt&&g[k]<a[i])k++;
        g[k]=a[i];
        if(k>cnt)cnt++;
    }
    
    cout<<cnt;
    
    
}

5

导弹防御系统  187. 导弹防御系统 - AcWing题库

贪心+dfs 有上一题的基础套上一个dfs即可

代码如下:

#include<iostream>
using namespace std;
const int N=55;
int a[N];
int n;
int T;
int ans;
int up[N],down[N];
void dfs(int u,int st1,int st2)
{
    if(st1+st2>=ans)return;
    if(u>n)
    {
        ans=st1+st2;
        return;
    }
    
    int k=1,t;
    while(k<=st1&&up[k]>=a[u])k++;
    t=up[k];
    up[k]=a[u];
    if(k>st1)dfs(u+1,st1+1,st2);
    else dfs(u+1,st1,st2);
    up[k]=t;
    
    
    
    k=1;
    while(k<=st2&&down[k]<=a[u])k++;
    t=down[k];
    down[k]=a[u];
    if(k>st2)dfs(u+1,st1,st2+1);
    else dfs(u+1,st1,st2);
    down[k]=t;
    
    
}
int main()
{
   
    while(cin>>n,n)
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        ans=n;
        dfs(1,0,0);
        cout<<ans<<endl;
    }
    
    
    
}

6

最长公共上升子序列

LCS+LIS 的耦合题

首先这道题的一个关键点是我们的a与b因为是等价的(公共子序列保证了)

所以用a来解决LCS   b保证LIS

这个题的集合划分和计算比较有意思,然后需要对n3优化

集合划分如下

 

暴力代码如下:

 

#include<iostream>
using namespace std;
const int N=3010;
int f[N][N];
int a[N],b[N];
int n;

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int j=1;j<=n;j++)scanf("%d",&b[j]);
    
    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])
             for(int k=0;k<j;k++)
              if(b[k]<b[j])f[i][j]=max(f[i-1][k]+1,f[i][j]);
             
        }
    }
    int res=-1;
    for(int i=1;i<=n;i++)res=max(res,f[n][i]);
    
    cout<<res;
}

这里看最内层的for循环其实就是在求我们的满足b[k]<a[i]的情况下的f[i-1][k]的最大值 发现与kj无关 只需要用一个变量维护这个前缀的最大值即可~~优化后如下: 

#include<iostream>
using namespace std;
const int N=3010;
int f[N][N];
int a[N],b[N];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int j=1;j<=n;j++)scanf("%d",&b[j]);
    
    for(int i=1;i<=n;i++)
    {
        int maxv=1;
        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],maxv);
            if(b[j]<a[i])maxv=max(maxv,f[i-1][j]+1);
            // if(a[i]==b[j])
            // {
            //     f[i][j]=max(f[i][j],1);
            //      for(int k=1;k<j;k++)//这里其实就是在求我们的满足b[k]<a[i]的情况下的
                                        //f[i-1][k]的最大值 发现与kj无关 只需要用一个变量维 
                                        //护这个前缀的最大值即可~~

            //   if(b[k]<a[i]])f[i][j]=max(f[i][j],f[i-1][k]+1);
            // }
            
        }
    }
    
    int res=-1;
    for(int i=1;i<=n;i++)res=max(res,f[n][i]);
    cout<<res;
}

参考:acwing 算法提高课 算法基础课

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值