题意:
给定圆周上的nnn堆石子,每一堆有wiw_{i}wi个石头,合并两堆的代价为两堆的石头总数之和,问合并所有堆为一堆的最小代价。
O(n2)O(n^2)O(n2)做法:
转移方程形如dp[i][j]=min(dp[i][k],dp[k+1][j])+w[i][j]dp[i][j]=min(dp[i][k],dp[k+1][j])+w[i][j]dp[i][j]=min(dp[i][k],dp[k+1][j])+w[i][j]的转移方程,可以用四边形不等式优化,将O(n3)O(n^3)O(n3)的复杂度降为O(n2)O(n^2)O(n2)。其中必须是minminmin,w[i][j]w[i][j]w[i][j]表示处理区间为[i,j][i,j][i,j]的贡献。
四边形不等式:
若l≤l′≤r′≤rl \leq l' \leq r' \leq rl≤l′≤r′≤r,有w[l′][r′]≤w[l][r]w[l'][r'] \leq w[l][r]w[l′][r′]≤w[l][r]或w[l][r′]+w[l′][r]≤w[l][r]+w[l′][r′]w[l][r']+w[l'][r]\leq w[l][r]+w[l'][r']w[l][r′]+w[l′][r]≤w[l][r]+w[l′][r′]成立,就有如下的不等式成立:
s[l][r−1]<=s[l][r]<=s[l][r+1]s[l][r-1]<=s[l][r]<=s[l][r+1]s[l][r−1]<=s[l][r]<=s[l][r+1],其中s[l][r]s[l][r]s[l][r]表示为[l,r][l,r][l,r]的最佳决策点,即使dp[l][k]+dp[k+1][j]dp[l][k]+dp[k+1][j]dp[l][k]+dp[k+1][j]最小的kkk
那么,我们计算dp[i][j]dp[i][j]dp[i][j]时,我们只需要在[s[i][j−1],s[i+1][j]][s[i][j-1],s[i+1][j]][s[i][j−1],s[i+1][j]]中枚举kkk即可,假设我们在j−ij-ij−i恒定的情况下枚举,也就是按顺序枚举dp[i][j],dp[i+1][j+1],dp[i+2][j+2],...,dp[n−(j−i+1)+1][n]dp[i][j],dp[i+1][j+1],dp[i+2][j+2],...,dp[n-(j-i+1)+1][n]dp[i][j],dp[i+1][j+1],dp[i+2][j+2],...,dp[n−(j−i+1)+1][n]
我们分别需要在
[s[i][j−1],s[i+1][j]][s[i][j-1],s[i+1][j]][s[i][j−1],s[i+1][j]]
[s[i+1][j],s[i+2][j+1]][s[i+1][j],s[i+2][j+1]][s[i+1][j],s[i+2][j+1]]
...............
[s[n−(j−i+1)+1][n−1],s[n−(j−i+1)+1−1][n]][s[n-(j-i+1)+1][n-1],s[n-(j-i+1)+1-1][n]][s[n−(j−i+1)+1][n−1],s[n−(j−i+1)+1−1][n]]
中枚举kkk,发现这些区间首尾相连,总长度为s[n−l+2][n]−s[1][l−1]+n−l+1≤n+n=2ns[n-l+2][n]-s[1][l-1]+n-l+1\leq n+n=2ns[n−l+2][n]−s[1][l−1]+n−l+1≤n+n=2n
所以我们给定计算区间的长度,可以在O(n)O(n)O(n)时间内计算完所有的这个长度的dpdpdp值,枚举长度也是O(n)O(n)O(n),总复杂度是O(n2)O(n^2)O(n2)
最大值:
性质:dp[i][j]dp[i][j]dp[i][j]的最大值总是在合并端点处取得,也就是max(dp[i+1][j],dp[i][j−1])max(dp[i+1][j],dp[i][j-1])max(dp[i+1][j],dp[i][j−1])
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,w[205],sum[205],dp[205][205],s[205][205];
int inf=0x3fffffff,ans=inf;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
sum[i]=sum[i-1]+w[i];
s[i][i]=i;
}
for(int i=n+1;i<=2*n;i++)
{
w[i]=w[i-n];
s[i][i]=i;
sum[i]=sum[i-1]+w[i];
}
auto getvalue=[&](int l,int r)
{
return sum[r]-sum[l-1];
};
for(int i=1;i<=2*n;i++)
{
// for(int j=1;j<=2*n;j++) dp[i][j]=inf;
dp[i][i]=0;
}
for(int i=2*n-1;i>=1;i--)
for(int j=i+1;j<=2*n;j++) dp[i][j]=min(dp[i+1][j],dp[i][j-1])+getvalue(i,j);
for(int i=1;i<=n;i++) ans=min(ans,dp[i][i+n-1]);
cout<<ans<<endl;
for(int i=2*n-1;i>=1;i--)
for(int j=i+1;j<=2*n;j++) dp[i][j]=max(dp[i+1][j],dp[i][j-1])+getvalue(i,j);
ans=-1;
for(int i=1;i<=n;i++) ans=max(ans,dp[i][i+n-1]);
cout<<ans;
return 0;
}
878

被折叠的 条评论
为什么被折叠?



