E28【模板】区间DP 石子合并

E28【模板】区间DP 石子合并——信息学竞赛算法_哔哩哔哩_bilibili

 

P1775 石子合并(弱化版) - 洛谷

[1,1] [2,2] [3,3] [4,4] [5,5]
[1,2] [2,3] [3,4] [4,5]
[1,3] [2,4] [3,5]
[1,4] [2,5]
[1,5]

[1,1] [1,2] [1,3] [1,4] [1,5]
        [2,2] [2,3] [2,4] [2,5]
                [3,3] [3,4] [3,5]
                        [4,4] [4,5]
                                [5,5]

// 区间DP O(n^3)
#include<bits/stdc++.h>
using namespace std;

const int N=310;
int n,a[N],s[N];
int f[N][N]; //f[i][j]表示把从i到j合并的最小代价 

int main(){
  memset(f,0x3f,sizeof(f));    
  cin>>n;
  for(int i=1;i<=n;i++)
    cin>>a[i],s[i]=s[i-1]+a[i],f[i][i]=0;
  
  for(int len=2; len<=n; len++)       //枚举区间长度 
    for(int i=1,j=len; j<=n; i++,j++) //枚举区间端点 
      for(int k=i; k<j; k++)          //枚举区间断点 
        f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);

  cout<<f[1][n];
}

 

P3146 [USACO16OPEN] 248 G - 洛谷

// 区间DP O(n^3)
#include<bits/stdc++.h>
using namespace std;

const int N=250;
int n,ans,f[N][N]; //f[i,j]表示把从i到j合并获得的最大值

int main(){
  scanf("%d",&n);
  for(int i=1; i<=n; i++){
    scanf("%d",&f[i][i]);
    ans=max(ans,f[i][i]);
  }
  
  for(int len=2; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      for(int k=i; k<j; k++){
        if(f[i][k]==f[k+1][j] && f[i][k]){ //两区间f均为0 不转移,例[1 2][3 4]
          f[i][j]=max(f[i][j],f[i][k]+1);
          ans=max(ans,f[i][j]);
        }
      }
    }
  }
  printf("%d\n",ans);        
  return 0;
} 

 

P4805 [CCC 2016] 合并饭团 - 洛谷

// 区间DP+双指针 O(n^3)
#include<bits/stdc++.h>
using namespace std;

int n,f[405][405],ans;

int main(){
  cin>>n;
  for(int i=1; i<=n; ++i){
    cin>>f[i][i];
    ans=max(ans,f[i][i]);
  }
  for(int len=2; len<=n; ++len){
    for(int i=1,j=len; j<=n; ++i,++j){
      for(int k=i; k<j; ++k){ //合并两个
        if(f[i][k] && f[i][k]==f[k+1][j]){
          f[i][j]=f[i][k]+f[k+1][j];
          break;
        }
      }
      for(int k=i,t=j; k<t-1; ){ //合并三个
        if(f[i][j]) break;
        if(!f[i][k]) ++k;
        else if(!f[t][j]) --t;
        else if(f[i][k]==f[t][j]){
          if(f[k+1][t-1]) f[i][j]=f[i][k]+f[k+1][t-1]+f[t][j];
          else ++k,--t;
        }
        else if(f[i][k]<f[t][j]) ++k;
        else if(f[i][k]>f[t][j]) --t;
      }
      ans=max(ans,f[i][j]);
    }
  }
  cout<<ans;
}

 

P2858 [USACO06FEB] Treats for the Cows G/S - 洛谷

满足最优子区间,所以区间DP,每次的断点只有两个

// 区间DP O(n^2)
#include<bits/stdc++.h>
using namespace std;

const int N=2005;
int n,a[N],f[N][N];

int main(){
  cin>>n;
  for(int i=1;i<=n;i++) cin>>a[i];
  
  for(int len=1; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      f[i][j]=max(f[i+1][j]+a[i]*(n-len+1),f[i][j-1]+a[j]*(n-len+1));
    } //len=1时最后一天卖 a*n,len=2时倒数第二天卖 a*(n-1)...
  }
  cout<<f[1][n];
}

 

P1005 [NOIP 2007 提高组] 矩阵取数游戏 - 洛谷

行间独立,每行满足最优子区间,所以区间DP,每次的断点只有两个

// 区间DP O(1000*n^2)
#include <bits/stdc++.h>
using namespace std;

const int N=82;
int n,m;
__int128 a[N][N],f[N][N],ans; //f[i,j]表示合并区间[i,j]获得的最大值

void input(__int128 &s){
  s=0; char c;
  while(c>'9'||c<'0') c=getchar();
  while(c>='0'&&c<='9'){
    s=s*10+c-'0';
    c=getchar();
  }
}
void output(__int128 x){
  if(x>9) output(x/10);
  putchar(x%10+'0');
}
__int128 work(__int128 a[]){
  memset(f,0,sizeof(f));
  for(int len=1; len<=m; ++len)
  for(int i=1,j=len; j<=m; ++i,++j)
    f[i][j]=max(f[i+1][j]+a[i]*((__int128)1<<m-len+1),f[i][j-1]+a[j]*((__int128)1<<m-len+1));
    // f[i][j]=max(a[i]+f[i+1][j],f[i][j-1]+a[j])*2; //类似秦九韶
  return f[1][m];
}
int main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++)
  for(int j=1;j<=m;j++) input(a[i][j]);
  
  for(int i=1;i<=n;i++) ans+=work(a[i]);
  output(ans);
}

 

P1435 [IOI 2000] 回文字串 - 洛谷

// 区间DP O(n^2)
#include<bits/stdc++.h>
using namespace std;

const int N=1005;
char a[N];
int n,f[N][N]; //f[i,j]把从i到j的字符串变为回文串的最少插入字符数

int main(){
  scanf("%s",a+1);
  n=strlen(a+1);

  for(int len=2; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      if(a[i]==a[j]) f[i][j]=f[i+1][j-1];
      else f[i][j]=min(f[i+1][j],f[i][j-1])+1;
    }
  }
  cout<<f[1][n];
}

 

P3847 [TJOI2007] 调整队形 - 洛谷

// 区间DP O(n^2)
#include<bits/stdc++.h>
using namespace std;

const int N=3005;
int n,a[N],f[N][N]; //f[i][j]表示从i到j对称的最小操作次数 

int main(){
  scanf("%d",&n);
  for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  
  for(int len=2;len<=n;len++){
    for(int i=1,j=len;j<=n;i++,j++){
      if(a[i]==a[j]) f[i][j]=f[i+1][j-1];
      else f[i][j]=min(min(f[i+1][j],f[i][j-1]),f[i+1][j-1])+1;
    }
  }
  cout<<f[1][n];
}

 

P9325 [CCC 2023 S2] Symmetric Mountains - 洛谷

// 区间DP O(n^2)
#include<bits/stdc++.h>
using namespace std;

const int N=5005;
int n,a[N];
int f[N][N],ans[N];

int main(){
  cin>>n;
  for(int i=1;i<=n;i++) cin>>a[i];
  memset(ans,0x3f,sizeof ans); ans[1]=0;
  
  for(int len=2; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      f[i][j]=f[i+1][j-1]+abs(a[i]-a[j]);
      ans[len]=min(ans[len],f[i][j]);
    }
  }
  for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}

 

P11838 [USACO25FEB] Printing Sequences B - 洛谷

// 区间DP O(n^4)
#include<bits/stdc++.h>
using namespace std;

const int N=110;
int T,n,m,a[N],f[N][N];

bool check(int l,int r,int k){ //k是否循环节
    for(int i=l;i<=r-k;i++)    if(a[i]!=a[i+k]) return false;
    return true;
}
int main(){
  cin>>T;
  while(T--){
    memset(f,0x3f,sizeof f);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i],f[i][i]=1;
    
    for(int len=2;len<=n;len++){
      for(int i=1,j=len;j<=n;i++,j++){
        for(int k=i;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
        for(int k=1;k<j;k++){
        // for(int k=1;k*k<=len;k++){
          if(len%k!=0) continue;
          if(check(i,j,k)) f[i][j]=min(f[i][j],f[i][i+k-1]);
          // if(check(i,j,len/k)) f[i][j]=min(f[i][j],f[i][i+len/k-1]);
        }
      }
    }
    cout<<(f[1][n]<=m?"YES\n":"NO\n");
  }
  return 0;
}

 

P5851 [USACO19DEC] Greedy Pie Eaters P - 洛谷

// 区间DP O(n^3)
#include<bits/stdc++.h>
using namespace std;

const int N=310;
int n,m;
int f[N][N],g[N][N][N];

int main(){
  scanf("%d%d",&n,&m);
  for(int i=1,w,l,r; i<=m; i++){
    scanf("%d%d%d",&w,&l,&r);
    for(int j=l; j<=r; j++) g[l][r][j]=w;
  }
  for(int len=1; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      for(int k=i; k<=j; k++)
        g[i][j][k]=max(g[i][j][k],max(g[i+1][j][k],g[i][j-1][k]));
    }
  }
  for(int len=1; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      for(int k=i; k<=j; k++)
        f[i][j]=max(f[i][j],f[i][k-1]+g[i][j][k]+f[k+1][j]);
    }
  }
  printf("%d\n",f[1][n]);
  return 0;
} 

 

P3080 [USACO13MAR] The Cow Run G/S - 洛谷

// 区间DP O(n^2)
#include<bits/stdc++.h>
using namespace std;

const int N=1005;
int n,c,d[N],f[N][N][2]; //f[i,j,0/1]是套住[i,j]的牛,站在了i点/j点的最少的花费

int main(){
  cin>>n;
  for(int i=1; i<=n; i++) cin>>d[i];
  d[++n]=0; sort(d+1,d+1+n);
  c=lower_bound(d+1,d+1+n,0)-d;
  
  memset(f,0x3f,sizeof f);
  f[c][c][1]=f[c][c][0]=0; 
  
  for(int len=2; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      int cnt=n-(j-i); //没套住的牛数
      f[i][j][0]=min(f[i+1][j][0]+(d[i+1]-d[i])*cnt, //从i+1走到i的花费
                     f[i+1][j][1]+(d[j]-d[i])*cnt);  //从j走到i的花费
      f[i][j][1]=min(f[i][j-1][0]+(d[j]-d[i])*cnt,   //从i走到j的花费
                     f[i][j-1][1]+(d[j]-d[j-1])*cnt);//从j-1走到j的花费
    }
  }
  cout<<min(f[1][n][0],f[1][n][1]);
  return 0;
}

 

P6006 [USACO20JAN] Farmer John Solves 3SUM G - 洛谷

// 区间DP O(n^2)
#include<bits/stdc++.h>
using namespace std;

const int N=5010,M=1e6;
int n,q,a[N],cnt[3000010];
long long f[N][N];

int main(){
  ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
  cin>>n>>q;
  for(int i=1;i<=n;i++) cin>>a[i];
  
  for(int i=1; i<=n; i++){
    for(int j=i+1; j<=n; j++){
      f[i][j]=cnt[a[i]+a[j]+M]; //f[i][j]:a[i]+a[j]+a[i+1~j-1]=0 的个数
      cnt[-a[j]+M]++;           //cnt:[i+1~j]内值为-a[j]的个数
    }
    for(int j=i+1; j<=n; j++) cnt[-a[j]+M]=0;
  }
  
  for(int len=3; len<=n; len++){
    for(int i=1,j=len; j<=n; i++,j++){
      f[i][j]=f[i+1][j]+f[i][j-1]-f[i+1][j-1]+f[i][j]; //平面内点的个数容斥
    }
  }
  while(q--){
    int a,b; cin>>a>>b;
    cout<<f[a][b]<<"\n";
  }
}

 

P3102 [USACO14FEB] Secret Code S - 洛谷

// 区间DP O(n^3)
#include<bits/stdc++.h>
#define mod 2014
using namespace std;

const int N=105;
char s[N];
int n,d[N][N][N]; //d[i,j,k]表示[i,i+k−1]和[j,j+k−1]是否相等,k为长度
int f[N][N];      //f[i,j]表示区间[i,j]内的方案数

int main(){
  scanf("%s",s+1);
  n=strlen(s+1);
  for(int i=1; i<=n; i++)
  for(int j=i; j<=n; j++)
  for(int k=1; k+i-1<=n && k+j-1<=n; k++)
    if(s[i+k-1]==s[j+k-1]) d[i][j][k]=1;
    else break;
  
  for(int i=1; i<=n; i++)
  for(int j=1; j<=n; j++) f[i][j]=1;
  
  for(int len=3; len<=n; len++)
  for(int i=1,j=len; j<=n; i++,j++)
  for(int k=i; k<j; k++){
    int a=k-i+1, b=j-k;
    if(a<b && d[i][k+1][a]) f[i][j]+=f[k+1][j];   //a段=b前段
    if(a<b && d[i][j-a+1][a]) f[i][j]+=f[k+1][j]; //a段=b后段
    if(a>b && d[i][k+1][b]) f[i][j]+=f[i][k];     //a前段=b段
    if(a>b && d[k-b+1][k+1][b]) f[i][j]+=f[i][k]; //a后段=b段
    f[i][j]%=mod;
  }

  printf("%d",f[1][n]-1);
}

 

P11676 [USACO25JAN] DFS Order P - 洛谷

// 区间DP O(n^3)
#include<bits/stdc++.h>
using namespace std; 

const int N=755; 
int n,c,a[N][N],s[N][N];
int f[N][N]; //f[i,j]表示以i为根的子树包含区间[i,j]的最小代价

int main(){
  ios::sync_with_stdio(0),cin.tie(0); 
  cin>>n;
  for(int j=2; j<=n; j++)
  for(int i=1; i<j; i++)
    cin>>a[i][j],c+=a[i][j]<0?-a[i][j]:0; //c所有已有边权之和
    
  for(int i=1; i<=n; i++)
  for(int j=i+1; j<=n; j++)
    s[i][j]=s[i][j-1]+(a[i][j]<0?a[i][j]:0); //s[i,j]以i为根的负边权的前缀和
  
  memset(f,0x3f,sizeof f);
  for(int i=1; i<=n; i++) f[i][i]=0;
  
  for(int len=2; len<=n; len++)
  for(int i=1,j=len; j<=n; i++,j++)
  for(int k=i; k<j; k++){
    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[i][j]-s[i][k+1]+a[i][k+1]);
    // printf("[%d %d]=%d\n",i,j,f[i][j]);
  }
  
  cout<<f[1][n]+c;
}

 

posted @ 2023-04-10 10:14  董晓  阅读(975)  评论(0)    收藏  举报