线性DP
线性DP
LIS最长上升子序列
——Longest increasing subsequence
转自blog
暴力:
为了上升我们肯定要知道我们当前阶段最后一个元素为多少,为了最长我们还要知道当前我们的序列有多长
设 \(F[i]\) 表示以 \(A[i]\) 为结尾的最长上升子序列的长度,
为了保证保证元素单调递增我们肯定只能从$ i$ 前面的且末尾元素比$ A[i]$ 小的状态转移过来
\[F[i]=max(f[j]+1)~(0<=j<i,a[j]<a[i])
\]
边转移转更新答案
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++)
if(a[j]<a[i])
f[i]=max(f[i],f[j]+1);
ans=max(ans,f[i]);
}
树状数组优化
原博客优化1代码是有锅的
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
const int N =103,INF=0x7f7f7f7f;
struct Node{
int val,id;
}a[N];
int c[N];
int n;
bool cmp(Node a,Node b) {
return a.val==b.val?a.id<b.id:a.val<b.val;
}
void upd(int x,int y) {
for(;x<=n;x+=x&(-x)) c[x]=max(c[x],y);
}
int query(int x) {
int res=0;
for(;x;x-=x&(-x)) res=max(res,c[x]);
return res;
}
int main() {
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i].val);
a[i].id=i;//有时要离散化
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++) {
int maxx=query(a[i].id);//查询编号小于等于id[i]的LIS最大长度
upd(a[i].id,++maxx);//把长度+1,再去更新前面的LIS长度
ans=max(ans,maxx);//更新答案
}
printf("%d\n",ans);
return 0;
}
贪心+二分查找优化
不上升g/下降f子序列
g[1]=f[1]=a[1];
for(int i=2;i<=n;i++){
if(g[now]>=a[i]) g[++now]=a[i];
else g[upper_bound(g+1,g+now+1,a[i],cmp())-g]=a[i];
if(f[con]<=a[i])f[++con]=a[i];
else f[upper_bound(f+1,f+con+1,a[i])-f]=a[i];
}
ans=now or con
LCS最长公共子序列
——longest common subsequence
公共子序列和公共子串的区别
公共子串是指连续的公有字符串,公共子序列是指两个字符串都任意删除0个或多个字符后得到的公有字符串,子序列可以是不连续的。
举个例子吧,有两个字符串,串A为“1 2 3 4 5 6 7”,串B 为“1 3 4 5 8 7”,很明显,A和B的公共子串为“3 4 5”,A和B的公共子序列可以是 “3 5”,或是“1 3 7”,等等。
最长公共子串:在两个字符串中找到一个最长的公共子串,要求子串在原串中是连续的。
最长公共子序列:在两个字符串中找到一个最长的公共子串,不要求子串在原串中是连续的。
f[i][0]=f[0][i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
f[i][j]=max(f[i][j],f[i-1][j]);
f[i][j]=max(f[i][j],f[i][j-1]);
if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
cout<<f[n][m];
LCIS最长公共上升子序列
https://www.cnblogs.com/WArobot/p/7479431.html
cnblogs.com/Howe-Young/p/5082611.html
题目:
守望者的逃离
贪心。。。然鹅我只70pts
能闪现肯定闪现
不能闪我讨论了好几种
放个傻代码记录一下
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int m,s,t;
int ans;
bool f;
int main() {
scanf("%d%d%d",&m,&s,&t);
int ans=t,ss=s;
while(s>0){
while(m>=10&&t>0){
if(s<=0) break;
s-=60,m-=10,t--;
}
while(m>=6&&s>17&&t>=2) {
t-=2;s-=60;m=m+4-10;
if(s<=0) break;
}
while(m>=2&&s>34&&t>=3){
m+=2,t-=3,s-=60;
if(s<=0) break;
}
if(s>=120&&t>=7) {s-=120;t-=7;}
while(t>0&&s<120) {
if(s<=0) break;
s-=17,t--;
}
if(t<=0&&s>0) {f=1;break;}
}
if(f) {
printf("No\n");
printf("%d\n",ss-s);
} else {
printf("Yes\n");
printf("%d\n",ans-t);
}
return 0;
}
题解巧妙地方法是把闪现和跑步分着存距离——谁大就要谁
然后枚举时间
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int m,s,t;
int s1,s2;
bool f;
int main() {
scanf("%d%d%d",&m,&s,&t);
for(int i=1;i<=t;i++) {
s1+=17;
if(m>=10) {s2+=60,m-=10;}
else m+=4;
if(s2>s1) s1=s2;
if(s1>s) {
puts("Yes");
printf("%d\n",i);
return 0;
}
}
puts("No");
printf("%d\n",s1);
return 0;
}
dp思想也很好理解
#include <cstdio>
#include <algorithm>
using namespace std;
int dp[300001];
int main()
{
int m, s, t;
scanf("%d%d%d", &m, &s, &t);
for (int i = 1; i <= t; i++)
{
if (m >= 10)
dp[i] = dp[i - 1] + 60, m -= 10;
else
dp[i] = dp[i - 1], m += 4;
}
for (int i = 1; i <= t; i++)
{
dp[i] = max(dp[i], dp[i - 1] + 17);
if (dp[i] >= s)
{
printf("Yes\n%d", i);
return 0;
}
}
printf("No\n%d", dp[t]);
return 0;
}
乌龟棋
一开始想着开 5 维,当场爆炸,然后又去写了爆搜
30骗到手
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int x[355],y[355];
int ans,res;
void dfs(int pos) {
if(pos==n) {ans=max(ans,res);return;}
if(pos>n) return;
for(int i=1;i<=4;i++){
if(y[i]==0) continue;
pos+=i;y[i]--;res+=x[pos];
dfs(pos);
res-=x[pos];pos-=i;y[i]++;
}
return;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&x[i]);
for(int i=1,u;i<=m;i++){
scanf("%d",&u);
y[u]++;
}
dfs(1);
printf("%d\n",x[1]+ans);
return 0;
}
然后刚点开题解就发现4维就可以。。。毕竟题目说最后肯定能到n
想啊想,终于想到了
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int x[355],y[355];
int ans,res;
int f[41][41][41][41];
inline void Max(int &x,int y){if(x<y) x=y;}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&x[i]);
for(int i=1,u;i<=m;i++){
scanf("%d",&u);
y[u]++;
}
f[0][0][0][0]=x[1];
for(int i=0;i<=y[1];i++) {
for(int j=0;j<=y[2];j++) {
for(int k=0;k<=y[3];k++) {
for(int l=0;l<=y[4];l++) {
int add=x[i+j*2+k*3+l*4+1];
if(i>0) Max(f[i][j][k][l],f[i-1][j][k][l]+add);
if(j>0) Max(f[i][j][k][l],f[i][j-1][k][l]+add);
if(k>0) Max(f[i][j][k][l],f[i][j][k-1][l]+add);
if(l>0) Max(f[i][j][k][l],f[i][j][k][l-1]+add);
}
}
}
}
printf("%d\n",f[y[1]][y[2]][y[3]][y[4]]);
return 0;
}