洛谷-P1880 石子合併(環形)

題目描述

在一個圓形操場的四周擺放N堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記爲該次合併的得分。

試設計出1個算法,計算出將N堆石子合併成1堆的最小得分和最大得分.

輸入輸出格式

輸入格式:

數據的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數.

輸出格式:

輸出共2行,第1行爲最小得分,第2行爲最大得分.

輸入輸出樣例

輸入樣例#1: 

4
4 5 9 4

輸出樣例#1: 

43
54

這道題目...開始本來都已經想到了利用一個區間變量然後從間隔爲1一直計算到間隔爲n-1...結果還是沒有做出來...

原因是 狀態轉移方程沒寫好...我最初的寫法是dp[i][j] = max(dp[i][j-1]+sz[j],dp[i+1][j]+sum[i])...真是失敗...唉....

 

這道題目很容易就能想到以間隔從小到大一層一層的來計算, 但是我沒有抓好狀態變化導致狀態轉移方程錯誤

因爲在間隔爲k的時候有很多中情況,我可以挑選一個間隔爲1另一個間隔爲k-1 也可以挑選間隔爲2另一個間隔爲k-2, 所以這些都要遍歷到, 所以在確定了區間i到j的情況下, 我們還需要從中找出和最大或者最小的那個區間,所以額外再用一個循環來遍歷.

因爲是環形數組, 但是因爲最大間隔爲n-1,  實際並不能遍歷到n*2以外的地方, 所以可以開一個兩倍大的數組,用直線模擬環

另一個點就是sum數組, 它是一個前綴和數組,  sum[i]表示從1到i的元素的和, 這樣要求區間i - j的和可以表示爲sum[j] - sum[i-1].

最後一個點就是在遞推遍歷的時候i要遍歷到2*n.

我之前用的是i<=n然後一直錯, 到後面頂點查錯的時候才發現, 在區間4-6區域, dp[4][6] = dp[4][4] + dp[5][6]+ sum[6]-sum[3].

其中因爲點只遍歷到n, 從而dp[5][6]的值並沒有計算出來,所以導致結果錯誤.

妙啊

另外還是

I HATE DP

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int sum[105];//前綴和
int sz[205];
int mindp[205][205],maxdp[205][205];
int n;
int main(){
    int tmp;
    cin>>n;
    memset(mindp,0,sizeof(mindp));
    memset(maxdp,0,sizeof(maxdp));
    sum[0] = 0;
    for(int i=1;i<=n;i++){
        cin>>tmp;
        sz[i] = sz[n+i] = tmp;
    }
    for(int i=1;i<=n*2;i++){
        sum[i] = sum[i-1]+sz[i];//sum是沒有錯誤的 
    }
    
    for(int j=1;j<n;j++){
        for(int i=1;i<=2*n;i++){
            int tp = i+j;
            mindp[i][tp]=0x3f3f3f3f;
            for(int k=i;k<tp&&k<=2*n;k++){
                maxdp[i][tp]=max(maxdp[i][tp],maxdp[i][k]+maxdp[k+1][tp]+sum[tp]-sum[i-1]);
                mindp[i][tp]=min(mindp[i][tp],mindp[i][k]+mindp[k+1][tp]+sum[tp]-sum[i-1]);
//				if(i == 4 && tp == 6){
//					cout<<"k: "<<mindp[i][k]<<" "<<mindp[k+1][tp]<<endl;
//					cout<<sum[tp]<<" "<<sum[i-1]<<" "<<mindp[i][tp]<<endl;
//					cout<<"********"<<endl;
//				}
    //錯誤原因  在計算4 - 6 這個區間內的結果時會遇到dp[4][4] + dp[5][6] 然而此時dp[5][6]並沒有賦值 
            }
        }
    }
    //測試 
//	for(int j=1;j<n;j++){
//		cout<<"j="<<j<<" : ";
//		for(int i=1;i<=n;i++){
//			int tp = i+j;
//			cout<<mindp[i][tp]<<" ";
//		}
//		cout<<endl;
//	}
    //
    int mi=0x3f3f3f3f,ma=0;
    for(int i=1;i<=n;i++){
//		cout<<mindp[i][i+n-1]<<" ";
        mi = min(mi,mindp[i][i+n-1]);
        ma = max(ma,maxdp[i][i+n-1]);
    }
//	cout<<endl;
//	for(int i=1;i<=n;i++){
//		cout<<maxdp[i][i+n-1]<<" ";
//	}
//	cout<<endl;
    cout<<mi<<"\n"<<ma<<endl;
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章