最玄学的dp。
放点水题吧
1.洛谷p1091 题意相当清晰,就是枚举中间最高人的位置
实现的时候发现了自已一直以来的理解失误。。
#include<iostream>
#include<cmath>
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,a[101];
int dp1[101],dp2[101];
//先找最长上升,再找最长下降
int work(int x){
for(int i=1;i<=n;i++){
dp1[i]=1;dp2[i]=1;
}
//dp[i]表示的以a[i]为结尾。so dp[n]未必是1-n种最大的dp
int m1=0;
for(int i=1;i<=x;i++){
for(int j=1;j<i;j++){
if(a[j]<a[i])
dp1[i]=max(dp1[i],dp1[j]+1);
}
m1=max(m1,dp1[i]);
}
int m2=0;
for(int i=x+1;i<=n;i++){
for(int j=x+1;j<i;j++){
if(a[j]>a[i]){
dp2[i]=max(dp2[i],dp2[j]+1);
}
}
m2=max(m2,dp2[i]);
}
return m1+m2;
}
int main(){
scanf("%d",&n);
int ans=10000000;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
ans=min(ans,n-work(i));
}
cout<<ans<<endl;
}
2.还是洛谷
p1280
题意:给k个区间,选取它们中不重叠的几个(如果能选不可不选),求最大覆盖长度(最小空域长度)
常规思想:问什么设什么dp,并且这里是倒序。感觉自己dp区间的选取都不太了解、
倒序方法 f[i]是指i-n时间内的休息时间
#include<iostream>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
vector <int> a[100005];
int dp[10002];
bool book[10003];
int n,k;
int main(){
cin>>n>>k;
for(int i=1;i<=k;i++){
int x,y;
cin >> x >>y;
a[x].push_back(y);
book[x]=1;
//book是记录工作点的,其实可以不用
}
for(int i=n;i>=1;i--){
//若不要工作,那么休息+1
if(!book[i]) dp[i]=dp[i+1]+1;
else{
//如果要的话,那么选区工作区间外的那一块比较
//终于看明白max()是选区考虑的意思了。。
for(int j=0;j<a[i].size();j++){
dp[i]=max(dp[i],dp[i+a[i][j]]);
}
}
}
cout<<dp[1]<<endl;
}
正序方法 f[i]是指1-i内的休息时间
#include<iostream>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
vector <int> a[100005];
int dp[10002];
int n,k;
int main(){
cin>>n>>k;
for(int i=1;i<=k;i++){
int x,y;
cin >> x >>y;
a[x].push_back(y);
}
for(int i=1;i<=n;i++)dp[i]=-4500;
dp[1]=0;
for(int i=1;i<=n;i++){
if(a[i].size()==0){
dp[i+1]=max(dp[i]+1,dp[i+1]);
}
else{
for(int j=0;j<a[i].size();j++){
//这里有点玄学的说。
int &x=dp[i+a[i][j]];
x=max(x,dp[i]);
//其实就是dp[i+a[i][j]]=max(dp[i+a[i][j]],dp[i]);
}
}
}
cout<<dp[n+1]<<endl;
}
3石子合并 链状变环状
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
i<k<j
o3
#include<iostream>
#include<cmath>
#include<string.h>
#include<stdio.h>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
int n;
int dp1[300][300];
int dp2[300][300];
int a[300];
int s[300];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
for(int i=1;i<=2*n;i++){
s[i]=s[i-1]+a[i];
}
for(int p=1;p<n;p++){
for(int i=1,j=i+p;(i<2*n)&&(j<2*n);i++,j=i+p){
dp2[i][j]=99999999;
for(int k=i;k<j;k++){
dp1[i][j]=max(dp1[i][j],dp1[i][k]+dp1[k+1][j]+s[j]-s[i-1]);
dp2[i][j]=min(dp2[i][j],dp2[i][k]+dp2[k+1][j]+s[j]-s[i-1]);
}
}
}
int m1=0,m2=9999999;
for(int i=1;i<=n;i++){
//这是很多个链 dp1[i][i+n-1]就是在以ai为首,ai+n-1结尾的链中的最值
m1=max(m1,dp1[i][i+n-1]);
m2=min(m2,dp2[i][i+n-1]);
}
cout<<m2<<endl<<m1<<endl;
}
/*
int dfs1(int l,int r){
if(dp1[l][r]) return dp1[l][r];
if(l==r) return dp1[l][r]=0;
int res=0;
for(int k=l;k<r;k++){
res=max(res,dfs1(l,k)+dfs1(k+1,r)+s[r]-s[l-1]);
}
return dp1[l][r]=res;
}
int dfs2(int l,int r){
if(dp2[l][r]) return dp2[l][r];
if(l==r) return dp2[l][r]=0;
int res=99999999;
for(int k=l;k<r;k++){
res=min(res,dfs2(l,k)+dfs2(k+1,r)+s[r]-s[l-1]);
}
return dp2[l][r]=res;
}
4.纪念一下自己第一次做的dp吧 纯水题
#include<iostream>
#include<cmath>
#include<algorithm>
#include<string>
using namespace std;
int a[1001][1001];
int dp[1001][1001];
int ans;
int n;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>a[i][j];
}
}
dp[1][1]=a[1][1];
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j];
}
}
for(int i=1;i<=n;i++)ans=max(ans,dp[n][i]);
cout<<ans<<endl;
}
5.多维dp模板 洛谷 方格取数&传纸条
#include<iostream>
#include<cmath>
#include<algorithm>
#include<string>
using namespace std;
int dp[20][20][20][20];
int n;
int a[10][10];
int max(int x,int y,int z,in/t k){
return max(max(x,y),max(k,z));
}
int main(){
cin>>n;
int x,y,z;
while(cin>>x>>y>>z){
if(x==0) break;
a[x][y]=z;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
int l=i+j-k;
if(l<=0)break;
//dp[i][j][k][l]是第一个人走到i,j;第二人到k,j。
//状态转移,讨论两个人上一步是从哪里走来
dp[i][j][k][l]=max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],
dp[i][j-1][k][l-1],dp[i][j-1][k-1][l]);
if(i==k&&j==l) dp[i][j][k][l]+=a[i][j];
else dp[i][j][k][l]+=a[i][j]+a[k][l];
}
}
}
cout<<dp[n][n][n][n]<<endl;
}
三维优化*1 寻找另一个变量代替一维 这里用的是步数
#include<iostream>
#include<cmath>
#include<algorithm>
#include<string>
using namespace std;
int dp[600][60][60];
int n,m;
int a[100][100];
int max(int x,int y,int z,int k){
return max(max(x,y),max(z,k));
}
int main(){
cin>>m>>n;
int x,y,z;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
for(int i=1;i<=m+n-1;i++){
for(int c=1;c<=m;c++){
for(int b=1;b<=m;b++){
dp[i][c][b]=max(dp[i-1][c-1][b-1],dp[i-1][c][b-1],dp[i-1][c-1][b],dp[i-1][c][b]);
if(c!=b)
dp[i][c][b]+=a[c][i-c+1]+a[b][i-b+1];
else dp[i][c][b]+=a[c][i-c+1];
}
}
}
cout<<dp[m+n-1][m][m]<<endl;
}
6.洛谷模板LCS
#include <cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<string>
using namespace std;
const int inf = 0x3f3f3f;
int n,len,a[100005],b[100005],f[100005];
int bound(int x){
int l=1,r=len;
while(l<r){
int mid=(l+r)>>1;
if(b[f[mid]]>b[x])
r=mid;
else l=mid+1;
}
return l;
}
int main(){
cin>>n;
//这是o2复杂度的简单解法 这道题里面不可用,数组会爆 ,tle
/*
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++){
dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
if(a[i]==b[j]){
dp[i][j]=dp[i-1][j-1]+1;
}
}
}
cout<<dp[n][n]<<endl;
*/
for(int i=1;i<=n;i++){
cin >> a[i];
}
for(int i=1;i<=n;i++){
int x;
cin>>x;
b[x]=i;
}
//数组b[i]里面存的是数字i在第二个数组中出现的位置
for(int i=1;i<=n;i++) f[i]=inf;
f[1]=0;
len=0;
for(int i=1;i<=n;i++){
//如当前数字的出现位置 在 当前答案序列最末尾数字的出现位置 之后: 接上
if(b[a[i]]>b[f[len]]){
f[++len]=a[i];
}
else{
//其实就是用二分找lower_bound的过程
//如当前数字的出现 在 答案末尾之前 :更新(替换掉大于等于它的第一个数字)
f[bound(a[i])]=a[i];
}
}
cout << len<<endl;
return 0;
}
7.2019 icpc ShangHai_H
题意:好长啊 告辞(其实蛮好懂的 当时竟然gg了)
一个相当标致的dp
这个悲伤的故事告诉我们 当你超时的时候,就不要想着剪枝了。
还有 记得取模。
我去qaq一会儿
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int t,n,a[500];
const int mod=1e9+7;
//dp[x][y]第x个数起 到最后一个能组成y的方案数
int dp[302][150005];
int sum,ans;
void ini(){
for(int i=1;i<=sum;i++) dp[n+1][i]=0;
return;
}
int main(){
cin >> t;
while(t--){
cin >> n;
sum=0; ans=0;
for(int i=1;i<=n;i++){
cin >> a[i];
sum+=a[i];
}
sort(a+1,a+1+n);
ini();
dp[n+1][0]=1;
for(int i=n;i>=1;i--){
for(int j=0;j<=sum;j++){
dp[i][j]=dp[i+1][j];
if(j>=a[i]){
dp[i][j]+=dp[i+1][j-a[i]];
if(j*2>=sum && j-a[i]<= sum-j) ans+=dp[i+1][j-a[i]];
ans%=mod;
}
dp[i][j]%=mod;
}
}
cout << ans << endl;
}
return 0;
}
7.好 新鲜出炉的树形dp(x 记忆化搜索
洛谷P1040加分二叉树
#include<iostream>
using namespace std;
int n;
//pre记录前序遍历 s记录所选的序号 p是每层最优解
int pre[50],s[35][35],p[35][35];
//记忆化搜索
int dfs(int l,int r){
if(l>r) return 1;
if(l==r){
s[l][r]=l;
return p[l][r]=pre[l];
}
int ans=0;
long long num=1;
//记忆化
if(p[l][r]) return p[l][r];
for(int i=l;i<=r;i++){
//枚举每一层的根节点,记录答案
long long t=dfs(l,i-1)*dfs(i+1,r)+pre[i];
if(ans<t){
ans=t;
num=i;
}
}
s[l][r]=num;
return p[l][r]=ans;
}
void search(int l,int r){
if(l>r) return;
cout<< s[l][r]<<" ";
search(l,s[l][r]-1);
search(s[l][r]+1,r);
return;
}
int main(){
cin >> n;
for(int i=1;i<=n;i++)cin>>pre[i];
cout<<dfs(1,n)<<endl;
search(1,n);
return 0;
}
8 洛谷P2258子矩阵
我觉得这是个好题 嗯
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include <cmath>
using namespace std;
//按行来DP(最后还是要dp呢)
int n,m,r,c;
int a[20][20];
int cnt=1;
//cc记录单独每列中上下元素的差的绝对值之和
//rc[i][j]记录第i行和第j行之间相邻元素的差的绝对值之和
int book[20],cc[20],rc[20][20];
int f[20][20];
void ini(){
//记得只能算选用过的嗷
for(int i=1;i<=m;i++){
cc[i]=0;
for(int j=1;j<r;j++)
cc[i]+=abs(a[book[j]][i]-a[book[j+1]][i]);
}
for(int i=2;i<=m;i++){
for(int j=1;j<i;j++){
rc[i][j]=0;
for(int k=1;k<=r;k++){
rc[i][j]+=abs(a[book[k]][i]-a[book[k]][j]);
}
}
}
}
int cmin;
int ans=0x3f3f3f3f;
void dp(){
for(int i=1;i<=m;i++){
cmin=min(c,i);
for(int j=1;j<=cmin;j++){
if(j==1){
f[i][j]=cc[i];
}
else if(i==j){
f[i][j]=cc[i]+f[i-1][j-1]+rc[i][j-1];
}
else{
f[i][j]=0x3f3f3f;
for(int k=j-1;k<i;k++)
f[i][j]=min(f[i][j],f[k][j-1]+cc[i]+rc[i][k]);
}
if(j==c){
ans=min(ans,f[i][j]);
}
}
}
}
void dfs(int x){
if(x>n){
ini();
dp();
return;
}
//剪枝
if(r-cnt==n-x){
book[cnt++]=x;
dfs(x+1);
book[cnt--]=0;
return;
}
//不取当前的这一行
dfs(x+1);
//取这一行
book[cnt++]=x;
dfs(x+1);
book[cnt--]=0;
return ;
}
int main(){
cin>>n>>m>>r>>c;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
dfs(1);
cout<<ans<<endl;
return 0;
}