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

发布时间 2023-06-07 19:46:16作者: o-Sakurajimamai-o

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

 

  1 //最长上升子序列问题
  2 #include<bits/stdc++.h>
  3 using namespace std;
  4 const int N=1e5+10;
  5 int f[N],a[N],n,res;
  6 int main()
  7 {
  8     cin>>n;
  9     for(int i=0;i<n;i++) cin>>a[i];
 10     for(int i=0;i<n;i++)
 11     {
 12         f[i]=1;
 13         for(int j=0;j<i;j++)
 14             if(a[i]>a[j])
 15             f[i]=max(f[i],f[j]+1);
 16     }
 17     for(int i=0;i<n;i++) res=max(res,f[i]);
 18     cout<<res;
 19     return 0;
 20 }
 21 //最长上升子序列优化
 22 //基于贪心做法
 23 //首先可以证明整个f数组是单调递增的,即对于任意的索引i < j,都有 f[i] < f[j]。反证法如下:
 24 //若f[i] == f[j],对于f[j]对应的长度为j的最长上升子序列,其倒数第2个元素值一定小于f[j],那么这个倒数第2个元素值可以作为f[i],则有f[i] < f[j]。
 25 //同理可证f[i] > f[j]的情形。
 26 //继续分析:
 27 //如已扫描序列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长度的更新以及尾元素的更新做铺垫。
 28 // 或者从反面考虑:如果我们f[i]存储的不是长度为i的最长上升子序列的尾元素,如f[3] = 5(存1 4 5的尾元素),那么扫描后续元素就无法最大化地延长上升序列,无法达到题目要求。
 29 // 注意在思考问题时,从集合的角度考虑问题,即每个f[i]首先是关于所有长度为i的上升序列的函数,其次,f[i]存的是这些上升序列尾元素的最小值。
 30 // 那么在扫描原数组的时候,对于第i个数,可以根据前i-1个数得到的f数组的结果,对f数组进行长度或内容的更新,具体表现如下:(设f数组的实际长度为cnt)
 31 // 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++.
 32 // 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的元素值变小,使得后面元素接到前面更具有可能)。
 33 #include<bits/stdc++.h>
 34 using namespace std;
 35 const int N=1e5+10;
 36 int n,m,k,res,f[N],q[N],a[N];
 37 int main()
 38 {
 39     cin>>n;
 40     for(int i=0;i<n;i++) cin>>a[i];
 41     q[0]=-2e9;
 42     for(int i=0;i<n;i++)
 43     {
 44         if(q[res]<a[i]) q[++res]=a[i];
 45         else{
 46             int l=0,r=res;
 47             while(l<=r){
 48                 int mid=l+r>>1;
 49                 if(q[mid]>=a[i]) r=mid-1;
 50                 else l=mid+1;
 51             }
 52             q[l]=a[i];
 53         } 
 54     }
 55     cout<<res;
 56     return 0;
 57 }
 58 //最长上升子序列合
 59 //f[i]是第i位的最大上升子序列合,设k为倒数第二位的话
 60 //那f[i]=f[k]+a[i],以此类推
 61 #include<bits/stdc++.h>
 62 using namespace std;
 63 const int N=1e5+10;
 64 int n,res,a[N],f[N];
 65 int main()
 66 {
 67     cin>>n;
 68     for(int i=0;i<n;i++) cin>>a[i];
 69     for(int i=0;i<n;i++)
 70     {
 71         f[i]=a[i];
 72         for(int j=0;j<i;j++)
 73         if(a[i]>a[j]) f[i]=max(f[i],f[j]+a[i]);
 74     }
 75     for(int i=0;i<n;i++) res=max(res,f[i]);
 76     cout<<res;
 77     return 0;
 78 }
 79 //怪盗基德滑翔翼
 80 #include<bits/stdc++.h>
 81 using namespace std;
 82 const int N=1e5+10;
 83 int n,res,a[N],q[N],len;
 84 int main()
 85 {
 86     cin>>n;
 87     q[0]=-2e9;
 88     for(int i=0;i<n;i++) cin>>a[i];
 89     for(int i=0;i<n;i++)
 90     {                                                               
 91         if(q[len]<a[i]) q[++len]=a[i];
 92         else{
 93              int l=0,r=n-1;
 94              while(l<=r){
 95                 int mid=l+r>>1;
 96                 if(q[mid]>=a[i]) r=mid-1;
 97                 else l=mid+1;
 98              }
 99             q[l]=a[i];
100         }
101     }
102     res=len,len=0;
103     memset(q,0,sizeof q);
104     q[0]=2e9;
105     for(int i=n-1;i>=0;i--)
106     {
107         if(q[len]<a[i]) q[++len]=a[i];
108         else{
109             int l=0,r=n-1;
110             while(l<=r){
111                 int mid=l+r>>1;
112                 if(q[mid]>=a[i]) r=mid-1;
113                 else l=mid+1;
114             }
115         q[l]=a[i];
116         }
117     }
118     cout<<max(res,len)<<endl;
119     return 0;
120 }
121 //登山,合唱队形:https://www.luogu.com.cn/problem/P1091
122 #include<bits/stdc++.h>
123 using namespace std;
124 const int N=1e5+10;
125 int f[N],g[N],n,m,res,a[N],t,q[N];
126 int main()
127 {
128     cin>>n;
129     for(int i=0;i<n;i++) cin>>a[i];
130     for(int i=0;i<n;i++)
131     {
132         f[i]=1;
133         for(int j=0;j<i;j++)
134         if(a[i]>a[j]) f[i]=max(f[i],f[j]+1);
135     }
136     for(int i=n-1;i>=0;i--)
137     {
138         g[i]=1;
139         for(int j=n-1;j>=i;j--)
140         if(a[i]>a[j]) g[i]=max(g[i],g[j]+1);
141     }
142     for(int i=0;i<n;i++) res=max(res,g[i]+f[i]-1);
143     cout<<res;//登山
144     cout<<n-res;//合唱队行
145     return 0;
146 }
147 //友好城市:https://www.luogu.com.cn/problem/P2782
148 //只需要对y从城市排序,然后求x的最长上升子序列就可以
149 #include<bits/stdc++.h>
150 using namespace std;
151 const int N=2e5+10;
152 int x,n,res,len=0,q[N],f[N];
153 struct node
154 {
155     int x,y;
156     bool operator<(const node&w)const
157     {
158         return y<w.y;
159     }
160 }city[N];
161 int main()
162 {
163     cin>>n;
164     for(int i=0;i<n;i++) cin>>city[i].x>>city[i].y;
165     sort(city,city+n);
166     q[0]=-2e9;
167     for(int i=0;i<n;i++)
168     {
169         if(q[len]<city[i].x) q[++len]=city[i].x;
170         else{
171             int l=0,r=len;
172             while(l<=r){
173                 int mid=l+r>>1;
174                 if(q[mid]>=city[i].x) r=mid-1;
175                 else l=mid+1;
176             }
177             q[l]=city[i].x;
178         }
179     }
180     cout<<len;
181     return 0;
182 }