dp思想简述:
最路径往后的子路径也一定是最优的(想数字三角形)存储最优子路径,一面计算与比较那些一定不会在最优路径中的路径
对于最优解问题,如果某个状态以前的过程会影响到以后的状态,不可以用贪心,这时候用dp或者搜索
贪心的方法首先要凭经验直觉大胆假设,最后谨慎求证(反证法 归纳法 a>=b&&b<=a 。。。。。。)
B3637 最长上升子序列
简要思路:
最长递增数列中除去后面的项的子数列,也一定是当前情况下的那个的最长递增数列,将比它小的所有点的最长数列都记录在一个数组里面,免去了算那些一定不可能的数列的时间
然后每一次检测到前面的更小的情况 就比较那几个数组元素最大的算进去
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e6+10;
int n;
int a[maxn], f[maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[i]=1;
}//输入数组
//f[i]=0
f[1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
if(a[i]>a[j]){
f[i]=max(f[i],f[j]+1);
}//store f[i]
}
}
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,f[i]);
}
printf("%d",ans);
return 0;
}
P1115 最大子段和
简单思路:
这题好像没有特定的套路,就是如果后面的数加上前面的数比它本身打的话 答案里有它的话 也一定有前面的数(加上就能比它打了 不加白不加)
具体来说就是:
假设答案里面有2 因为-4+2比-4大 所以如果-4在答案里面 就一定也有2
现在答案是(2,-4)
因为3加上2-4等于1比自己小 所以如果3在答案里面 则一定不会和前面的2和-4同时在答案里面
也就是说答案就此断开了(2,-4)以及(3。。。)
-1+3大于-1本身 (3,-1)
2+3-1大于它本身 (3,-1,2)
-4加上3+2-1+2大于它本身 所以-4在答案里面的haul 前面三个也一定在答案里
(3,-1,2,-4)可-4不一定在答案里面
3由于加上前面的没变化 所以可在可不在
(3,-1,2,-4,3)
所以这个程序逻辑就大概解决
在不在一个合法答案里面 以及这个答案的最大值情况
先看是否在一个答案里面:
合法答案 (2,-4)和(3,-1,2,-4) 因为有-1比有3 所以有则2必有前面两个 -4同样
所以 答案会是(2,-4)(3,-1)(3,-1,2)(3,-1,2,4)(2)(3)中最大的
不会是别的子序列!!!!!!
那就从前往后扫 最后得到一个最大的就可以
判断元素a[i]本身与元素加上b[i-1](前面序列的加数) 的大小 选出那个较大的 存到b[i] 最终得到的b是上述6个合法序列所有的和
答案就是这六个和里面最大的那一个
#include <bits/stdc++.h>
#include <algorithm>
using namespace std;
int n,a,b;
int ans=-2147483647;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a;
if(i==1) b=a;
else b=max(a,a+b);
ans = max(ans,b);
}
cout<<ans;
return 0;
}
P8707 走方格
只能往下走和往右走,所以到某个点的路径数量取决于从起始位置到左边和上面的点的路径数量
但是 有些点不能走 而且不是所有点都有更左边的点和更上面的点
对于这两个问题: 标记走不到的点,在遇到走不了的点时就直接结束它的循环
然后针对在轴上左上不全的点 if判断如果不在轴上才能加上左边或者上面点走过的路径数量,在轴上就其中一个没有了
#include <iostream>
using namespace std;
int n,m;
int a[35][35];
int f[1000][1000];
int main(){
cin>>n>>m;
if(n%2==0 && m%2==0){
return 0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i%2==0 && j%2==0){
a[i][j]=1;
}else a[i][j]=0;
}
}
f[1][1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1){
continue;
}
if(i!=1)f[i][j]+=f[i-1][j]; //如果不在x轴上
if(j!=1)f[i][j]+=f[i][j-1];//如果不在y轴上
}
}
cout<<f[n][m];
return 0;
}
P1216 数字三角形 Number Triangle
简要思路:
从一个节点选择接下来的路径的时候 为了保证整个路径走下来是所有之中最大的
底下所选择的子节点所走到结尾的路径应该是所有选择中最大的
也就是 子路径是当下选择中最大的
所以可以从下到上存储所有路径中最小路径 用二维数组代表节点中的最短路径数
这样可以免去比较与计算那些一定不会用上的路径的时间
代码:
#include <iostream>
#include <algorithm>
#define maxn 1010
using namespace std;
int n;
int a[maxn][maxn],f[maxn][maxn];
int ans;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n-1;i++){
f[n-1][i]=a[n-1][i]+max(a[n][i],a[n][i+1]);
}
for(int i=n-2;i>=1;i--){
for(int j=1;j<=i;j++){
f[i][j]=a[i][j]+max(f[i+1][j],f[i+1][j+1]);
}
}
cout<<f[1][1];
return 0;
}
P1020 导弹拦截
前面的那一问是最长不增序子序列
后面那一问再考Dilworth定理 将一个序列剖成若干个单调不升子序列的最小个数等于该序列最长上升子序列的个数
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,r1,r2;
int a[maxn],b[maxn],c[maxn];
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
while (scanf("%d",a+(++n))!=EOF);--n;
b[1]=c[1]=a[1];
r1=r2=1;
for(int i=2;i<=n;++i){
if(b[r1]>=a[i]) b[++r1]=a[i];
else *upper_bound(b+1,b+r1+1,a[i],greater<int>())=a[i];
if(c[r2]<a[i]) c[++r2]=a[i];
else *lower_bound(c+1,c+r2+1,a[i])=a[i];
} printf("%d\n%d",r1,r2);
return 0;
}