HRBUST 1818/1819 石子合併問題--直線版 /圓形版(經典動態規劃)
阿新 • • 發佈:2019-02-10
石子合併問題(直線版)
一條直線上擺放著一行共n堆的石子。現要將石子有序地合併成一堆。規定每次只能選相鄰的兩堆合併成新的一堆,並將新的一堆石子數記為該次合併的得分。請編輯計算出將n堆石子合併成一堆的最小得分和將n堆石子合併成一堆的最大得分。
Input
輸入有多組測試資料。
每組第一行為n(n<=100),表示有n堆石子,。
二行為n個用空格隔開的整數,依次表示這n堆石子的石子數量ai(0<ai<=100)
Output
每組測試資料輸出有一行。輸出將n堆石子合併成一堆的最小得分和將n堆石子合併成一堆的最大得分。 中間用空格分開。
Sample Input
3
1 2 3
Sample Output
9 11
題解:經典的區間DP題。
以求最小值為例:
狀態dp[i][j]:i到j的合併最小值。
狀態轉移方程:dp[i][j]=min(dp[i][j],dp1[i][k]+dp[k+1][j]+num[j]-num[i-1])。
num[i]表示前i個元素的和。
答案:dp[1][n]。
PS:dp[i][j]要初始化為無窮大,但是當i==j時,要初始化為0。因為當按照i,j去遍歷的話,當求dp[i][j]的時候,i=k,dp[i][k]是沒有意義的,所有要dp[i][i]=0。當求dp[i][j]的時候還有一個問題就是dp[k+1][j]可能還是沒有求出來的,所有不能按照這樣去遍歷。可以按照區間的長度len去遍歷,j=len+i-1.
具體程式碼如下:
#include<iostream> #define INF 0x3f3f3f3f//無窮大 using namespace std; int dp1[102][102];//最小 int dp2[102][102];//最大 int n; void init() { for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) if(i==j) { dp1[i][j]=0; dp2[i][j]=0; } else { dp1[i][j]=INF; dp2[i][j]=0; } } int max(int a,int b) { return a>b?a:b; } int min(int a,int b) { return a<b?a:b; } int main() { while(cin>>n) { init(); int i,j,k,len; int num[102]={0};//前i項和 int a; for(i=1;i<=n;i++) { cin>>a; num[i]=num[i-1]+a; } for(len=2;len<=n;len++) for(i=1;i<n;i++) { j=len+i-1; // dp1[i][j]=INF;//前面不用init()函式的話,可以用這兩行初始化,效果一樣 // dp2[i][j]=0; if(j<=n) for(k=i;k<j;k++) { dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+num[j]-num[i-1]); dp2[i][j]=max(dp2[i][j],dp2[i][k]+dp2[k+1][j]+num[j]-num[i-1]); } } cout<<dp1[1][n]<<" "<<dp2[1][n]<<endl; } return 0; }
石子合併問題(圓形版)
題目跟直線版就改變了一小點,就是將石子鋪在圓形軌道上,求最大值,最小值。
題解:博主的方法有點偏暴力。將直線連著鋪兩次在一條直線上,就轉化成了直線版了。算出從1到2n的dp值,再比較出所有區間長度為n的dp的最大或最小值。區間長度為n的一共為n組。
具體程式碼如下:
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
int dp1[204][204];//最小
int dp2[204][204];//最大
int n;
void init()
{
for(int i=0;i<=n*2;i++)
for(int j=0;j<=n*2;j++)
{
if(i==j)
{
dp1[i][j]=0;
dp2[i][j]=0;
}
else{
dp1[i][j]=INF;
dp2[i][j]=0;
}
}
}
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
while(cin>>n)
{
// init();
int i,j,k,len;
int num[204]={0};//前i項和
int a[204];
a[0]=0;
for(i=1;i<=n;i++)
{
cin>>a[i];
num[i]=num[i-1]+a[i];
}
for(j=1;j<=n;j++)
{
num[i]=num[i-1]+a[j];
i++;
}
for(len=2;len<=n*2;len++)//區間長度要變
for(i=1;i<n*2;i++)
{
j=len+i-1;
dp1[i][j]=INF;
dp2[i][j]=0;
if(j<=n*2)
for(k=i;k<j;k++)
{
dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+num[j]-num[i-1]);
dp2[i][j]=max(dp2[i][j],dp2[i][k]+dp2[k+1][j]+num[j]-num[i-1]);
}
}
int MAX=0,MIN=INF;
for(i=1;i<=n;i++)
{
MAX=MAX>dp2[i][i+n-1]?MAX:dp2[i][i+n-1];
MIN=MIN<dp1[i][i+n-1]?MIN:dp1[i][i+n-1];
}
cout<<MIN<<" "<<MAX<<endl;
}
return 0;
}