傳送門:https://hihocoder.com/problemset/problem/1636
題意:
給你n(<=100)堆石子的數量,再給你兩個數L,R(2<=L<=R<=n),表示你只能將連續的x(L<=x<=R)堆石子合併成爲一堆,費用爲這x堆石子數量的總數。求將這n堆石子合併成1堆的最小花費,如果不能合併成一堆輸出0。
思路:這題跟區間dp的入門很像,但是暴力枚舉[i,k][k+1,j]區間的同時還要考慮兩個子區間的石子堆數,這樣dp需要再加一維,直接枚舉的複雜度就成了O(n^5),果然TLE了,然後窩就自閉了三個小時。。。
發現其實只需要考慮其中一個子區間爲1堆,另一個子區間爲s堆的情況,因爲多堆也會被包含進狀態裏面。
dp[i][j][s]表示第i堆石子到第j堆石子合併成爲s堆石子的最小花費。則有:
dp[i][j][s+1]=min(dp[i][j][s+1],dp[i][k][s]+dp[k+1][j][1]);(1<=s+1<=j-i+1 , i<=k<j)
dp[i][j][1]=min(dp[i][j][1],dp[i][k][s]+dp[k+1][j][1]+sum[j]-sum[i-1]);(1<=L<=s+1<=min(j-i+1,R) , i<=k<j)
dp[i][j][s+1]=min(dp[i][j][s+1],dp[i][k-1][1]+dp[k][j][s]);(1<=s+1<=j-i+1 , i<k<=j)
dp[i][j][1]=min(dp[i][j][1],dp[i][k][s]+dp[k+1][j][1]+sum[j]-sum[i-1]);(1<=L<=s+1<=min(j-i+1,R) , i<k<=j)
這樣時間複雜度就成了O(n^4),實際上還要除上一些常數,所以效率還是很高的。
代碼:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define mst(head,x,n) memset(head+1,x,n*sizeof(head[0]))
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int maxn=2e2+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
int n,m,k,L,R;
int a[maxn],sum[maxn];
int ans,tmp,cnt;
int flag;
char s[maxn];
int as[maxn];
bool ok[maxn];
int dp[maxn][maxn][maxn];
template <typename T>
inline void read(T &X){
X=0;int w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
if(w) X=-X;
}
int main(){
srand(time(NULL));
int T,cas=1;
//read(T);
//while(T--)
while(scanf("%d%d%d",&n,&L,&R)!=EOF)
{
//read(n);read(L);read(R);
ans=0; flag=0;
rep(i,1,n){
//read(a[i]);
scanf("%d",&a[i]);
//a[i]=rand()%1000+1;
sum[i]=sum[i-1]+a[i];
}
rep(i,0,n)
rep(j,0,n)
rep(k,0,n) dp[i][j][k]=inf;
rep(i,1,n) {
dp[i][i][1]=0;
}
rep(len,2,n){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
for(int k=i;k<j;k++){
for(int s1=1;s1<=k-i+1;s1++){
dp[i][j][s1+1]=min(dp[i][j][s1+1],dp[i][k][s1]+dp[k+1][j][1]);
if(s1+1>=L&&s1+1<=R)
dp[i][j][1]=min(dp[i][j][1],dp[i][k][s1]+dp[k+1][j][1]+sum[j]-sum[i-1]);
}
}
for(int k=i+1;k<=j;k++){
for(int s2=1;s2<=j-k+1;s2++){
dp[i][j][s2+1]=min(dp[i][j][s2+1],dp[i][k-1][1]+dp[k][j][s2]);
if(s2+1>=L&&s2+1<=R)
dp[i][j][1]=min(dp[i][j][1],dp[i][k-1][1]+dp[k][j][s2]+sum[j]-sum[i-1]);
}
}
}
}
//cout<<n<<" "<<L<<" "<<R<<endl;
if(dp[1][n][1]==inf) {
puts("0");
}
else {
printf("%d\n",dp[1][n][1]);
}
}
return 0;
}