动态规划五
复健\(Day4\)
动态规划(五)区间\(DP\)
\(1.\)石子合并
https://www.acwing.com/problem/content/284/
这是区间\(DP\)的模板题
这道题看似和果子合并很相像,于是我们想到贪心,但是这是行不通的
因为我们每次只能合并相邻的两堆,可以在纸上画一下样例,显然是不对的
\(f[l][r]\)表示把从\(l\)到\(r\)合并成一堆的最小代价:\(f[l,r]=f[l][k]+f[k+1][r]+s[r]-s[l-1]\)
边界:\(f[i][i]=0\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 310
#define inf 0x3f3f3f3f
using namespace std;
int f[maxn][maxn];
int s[maxn];
int a[maxn];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) s[i]=s[i-1]+a[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 l=1;l+len-1<=n;l++)
{
int r=l+len-1;
for(int k=l;k<r;k++)
{
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
}
}
}
printf("%d\n",f[1][n]);
return 0;
}
\(2.\)环形石子合并
https://www.luogu.com.cn/problem/P1880
展环为链即可
注意最后输出不是\(f[1][n]\)和\(g[1][n]\),而是要枚举起点,取最大值和最小值
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 310
#define inf 0x3f3f3f3f
using namespace std;
int f[maxn][maxn];
int g[maxn][maxn];
int s[maxn];
int a[maxn];
int main()
{
int n;
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];
memset(f,0x3f,sizeof(f));
memset(g,-0x3f,sizeof(g));
for(int i=1;i<=2*n;i++) f[i][i]=0,g[i][i]=0;
for(int len=2;len<=n;len++)
{
for(int l=1;l+len-1<=2*n;l++)
{
int r=l+len-1;
for(int k=l;k<r;k++)
{
f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
g[l][r]=max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
}
}
}
int maxx=0,minv=inf;
for(int i=1;i<=n;i++) minv=min(minv,f[i][i+n-1]),maxx=max(maxx,g[i][i+n-1]);
printf("%d\n%d\n",minv,maxx);
return 0;
}
\(3.\)能量项链
https://www.luogu.com.cn/problem/P1063
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 210
using namespace std;
int f[maxn][maxn];
int a[maxn];
signed main()
{
int n;
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++) f[i][i]=0;
for(int len=2;len<=n;len++)
{
for(int l=1;l+len-1<=2*n;l++)
{
int r=l+len-1;
for(int k=l;k<r;k++)
{
f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]+a[l]*a[k+1]*a[r+1]);
}
}
}
int maxx=0;
for(int i=1;i<=n;i++) maxx=max(maxx,f[i][i+n-1]);
printf("%d\n",maxx);
return 0;
}
注意\(l\)的终止条件是\(l+len-1<=2\times n\),如果我写成\(l\leqslant 2\times n\),会可能让数组越界,而且时间复杂度也变高了
正因无所有,才会无所畏