快乐dp

最玄学的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;
} 
发布了24 篇原创文章 · 获赞 2 · 访问量 967

猜你喜欢

转载自blog.csdn.net/weixin_43521836/article/details/89762019
今日推荐