ybtoj「动态规划」第2章 区间DP
区间dp经典题
设 \(f_{l,r}\) 表示合并区间 \([l,r]\) 的最小花费
考虑如何转移:可以枚举断点 $k\ $ \(f_{l,r}=f_{l,k}+f_{k+1,r}+a[\text{l~r}]\)
可以维护前缀和 \(f_{l,r}=f_{l,k}+f_{k+1,r}+sum[r]-sum[l-1]\)
复杂度 \(O(n^3)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
const int N=1e3+5;
inl int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int n,a[N],sum[N],ma[N][N],mi[N][N],ans1=0x3f3f3f3f,ans2;
signed main(){
n=read();
for(int i=1;i<=n;i++)a[i]=a[i+n]=read();
for(int i=1;i<=(n<<1);i++)sum[i]=sum[i-1]+a[i];
memset(mi,0x3f,sizeof(mi));
for(int i=1;i<=(n<<1);i++)mi[i][i]=0;
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=(n<<1);l++){
int r=l+len-1;
for(int k=l+1;k<=r;k++){
mi[l][r]=min(mi[l][r],mi[l][k-1]+mi[k][r]+sum[r]-sum[l-1]);
ma[l][r]=max(ma[l][r],ma[l][k-1]+ma[k][r]+sum[r]-sum[l-1]);
}
}
}
for(int i=1;i<=n;i++){
ans1=min(ans1,mi[i][i+n-1]);
ans2=max(ans2,ma[i][i+n-1]);
}
printf("%d\n%d",ans1,ans2);
return 0;
}
设 \(f_{l,r}\) 表示涂完 \([l,r]\) 的最小花费
初始状态:\(f_{i,i}=1\)
-
首先考虑白嫖:如果 \(col[l]=col[l+1]\) 或 \(col[r]=col[r-1]\) 涂上一次的时候多带一个 \(l\ 或\ r\)
即 \(f_{l,r}=f_{l+1,r}\ 或\ f_{l,r}=f_{l,r-1}\) -
白嫖不了枚举断点:\(f_{l,r}=f_{l,k}+f_{k+1,r}\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
const int N=1e3+5;
inl int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int n,f[N][N];
char s[55];
signed main(){
s[++n]=getchar();
while(s[n]>='A'&&s[n]<='Z')s[++n]=getchar();n--;
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++)f[i][i]=1;
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
if(s[l]==s[r]){
f[l][r]=min(f[l][r-1],f[l+1][r]);
continue;
}
for(int k=l+1;k<=r;k++){
f[l][r]=min(f[l][r],f[l][k-1]+f[k][r]);
}
}
}
printf("%d\n",f[1][n]);
return 0;
}
由于每个颜色必然一次性全消 那么可以预处理出颜色段数量和长度
设 \(f_{l,r}\) 表示消完 \([l,r]\) 的最高得分 发现如果 \(r\) 后还有和它颜色一样的可能会影响答案
于是加进状态:\(f_{l,r,k}\) 表示消完 \([l,r]\) 后面连着 \(k\) 个和 \(r\) 颜色一样的块 的最高得分
-
可以把右边直接消掉:\(f_{l,r,k}=f_{l,r-1,0}+f_{r,r,k}\)
-
否则会尽量让几段同颜色连起来:\(f_{l,r,k}=f_{l,p,k+len[r]}+f_{p+1,r-1,0}\ (col[p]=col[r])\)
代码是记搜
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define ll long long
#define ls k<<1
#define rs k<<1|1
#define mid (l+r>>1)
const int N=205;
const int M=1e3+5;
inl int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int T,n,m,a[N],col[N],len[N],f[N][N][M];
int dfs(int l,int r,int k){
if(l==r)return pow(len[r]+k,2);
if(f[l][r][k]>=0)return f[l][r][k];
int ans=dfs(l,r-1,0)+dfs(r,r,k);
for(int i=l;i<=r-1;i++){
if(col[i]^col[r])continue;
ans=max(ans,dfs(l,i,k+len[r])+dfs(i+1,r-1,0));
}
return f[l][r][k]=ans;
}
signed main(){
T=read();
for(int t=1;t<=T;t++){
n=read();m=0;
for(int i=1;i<=n;i++){
a[i]=read();
if(a[i]^a[i-1]){
col[++m]=a[i];
len[m]=1;
}else len[m]++;
}
memset(f,-0x3f,sizeof(f));
printf("Case %d: %d\n",t,dfs(1,m,0));
}
return 0;
}
设 \(f_{l,r}\) 表示消区间 \([l,r]\) 得到的最大价值
类似石子合并 维护 \(b\) 数组的前缀和
-
最优情况:\([l+1,r-1]\) 被消完( \(f_{l+1,r-1}=b_{r-1}-b_l\) ) 且 \(a[l]+a[r]\le k\) 那么直接全消:\(f_{l,r}=b_r-b_{l-1}\)
-
否则分段消:\(f_{l,r}=f_{l,k}+f_{k+1,r}\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
const int N=805;
inl int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,k,a[N];
ll b[N],f[N][N];
signed main(){
n=read();k=read();
for(int i=1;i<=n;i++)
a[i]=read(),b[i]=b[i-1]+read();
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
if(f[l+1][r-1]==b[r-1]-b[l]&&a[l]+a[r]<=k){
f[l][r]=b[r]-b[l-1];
continue;
}
for(int k=l;k<r;k++)
f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]);
}
}
printf("%lld\n",f[1][n]);
return 0;
}
方差的柿子推一下就会发现答案只与 \(\sum x_i\) 有关
二维区间dp 枚举横竖的切割线即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
const int N=20;
inl int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,a[N][N],f[N][N][N][N][N];
inl int sum(int lx,int ly,int rx,int ry){
int ans=0;
for(int i=lx;i<=rx;i++)
for(int j=ly;j<=ry;j++)
ans+=a[i][j];
return ans*ans;
}
inl double x_(){
double ans=0;
for(int i=1;i<=8;i++){
for(int j=1;j<=8;j++){
ans+=a[i][j];
}
}
return ans/n;
}
signed main(){
n=read();
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
a[i][j]=read();
memset(f,0x3f,sizeof(f));
for(int len1=1;len1<=8;len1++){
for(int lx=1;lx+len1-1<=8;lx++){
int rx=lx+len1-1;
for(int len2=1;len2<=8;len2++){
for(int ly=1;ly+len2-1<=8;ly++){
int ry=ly+len2-1;
f[lx][ly][rx][ry][0]=sum(lx,ly,rx,ry);
}
}
}
}
for(int k=1;k<=n-1;k++){
for(int len1=1;len1<=8;len1++){
for(int lx=1;lx+len1-1<=8;lx++){
int rx=lx+len1-1;
for(int len2=1;len2<=8;len2++){
for(int ly=1;ly+len2-1<=8;ly++){
int ry=ly+len2-1;
for(int kx=lx;kx<rx;kx++)
f[lx][ly][rx][ry][k]=min(f[lx][ly][rx][ry][k],min(f[lx][ly][kx][ry][k-1]+f[kx+1][ly][rx][ry][0],f[lx][ly][kx][ry][0]+f[kx+1][ly][rx][ry][k-1]));
for(int ky=ly;ky<ry;ky++)
f[lx][ly][rx][ry][k]=min(f[lx][ly][rx][ry][k],min(f[lx][ly][rx][ky][k-1]+f[lx][ky+1][rx][ry][0],f[lx][ly][rx][ky][0]+f[lx][ky+1][rx][ry][k-1]));
}
}
}
}
}
printf("%.3lf\n",sqrt((double)f[1][1][8][8][n-1]/n-pow(x_(),2)));
return 0;
}

浙公网安备 33010602011771号