藍橋杯 試題 算法提升 宰羊 DP解決

問題描述
  炫炫回了內蒙,確定要吃羊肉啦,全部他家要宰羊吃。
  炫炫家有N只羊,羊圈排成一排,標號1~N。炫炫天天吃掉一隻羊(這食量!實際上是放生啦),吃掉的羊的鄰居會覺得它被放生了,而後又會告訴他們的鄰居,這樣一直傳播下去,除非某個鄰居已經被「放生」了。每一天,全部知道某羊被「放生」了這個消息的羊都會很不滿,若是不給他們巧克力的話,他們就會很造反,炫炫已經知道他要吃掉哪些羊,他能夠任意安排吃的順序,而後使巧克力的用量最小,請求出這個最小值。
輸入格式
  本題有多組數據,第一行爲數據組數T。
  對於每組數據
  第一行:兩個用空格隔開的整數:N和M,表示羊的數量和須要吃掉的數量
  第二行:有M個數,表示要吃那些羊。
輸出格式
  T行,爲每組數據的答案。
樣例輸入
2
8 1
3
20 3
3 6 14
樣例輸出
7
35

解題思路:解這道題的關鍵是,當放生(或吃掉)一個位置的羊後,此位置的左端和右端就相互獨立了,即原問題變成了兩個規模更小獨立的子問題。要求原問題的最優解,則其子問題也必須是最優。(能夠
用"剪切-粘貼"("cut-and-paste")技術證實,即若是子問題的解若是不是最優,則能夠選擇其最優解代替非最優解(剪切非最優解,粘貼最優解) )。因此此題能夠用DP解決。
釋放K位置羊所需的巧克力數:
  •此時所需的巧克力數
  •釋放位置左側所需巧克力數  
  •釋放位置右側所需巧克力數
設數組A[]保存要被放生羊的位置,dp[ i ][ j ]:將A[ i ] 到 A[ j ] 連續部分的羊都放生後所需最小巧克力數。dp[ i ] [ j ]  = min( dp[ i ][ j ] , dp[ i ][ k ] + dp[ k ][ j ] ), dp[ i ][ j ] +=  A[ j ] - A[ i ] - 2
 
//完整代碼
 1 #include<cstdio>
 2 #include<climits>//INT_MAX
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int Max_M = 100;
 7 
 8 //輸入
 9 int T,N,M;
10 int A[Max_M+2]; //A下標: 0 - M+1 保存要吃掉羊的位置 
11 
12 int dp[Max_M+1][Max_M+2];//i:0-M j:0-M+1 dp數組 
13 
14 void solve()
15 {
16     A[0] = 0; //由於dp[i][j]不包含兩端,因此爲統一求解 
17     A[M+1] = N+1;//最後答案即 dp[0][N+1] 因此額外加上這兩個位置
18     
19     //初始化dp數組 
20     for(int m=0; m<=M; m++){
21         dp[m][m+1] = 0;
22     } 
23     
24     //從短區間開始填充dp數組 w:區間長度 
25     for(int w=2; w<=M+1; w++)
26     {
27         for(int i=0; i+w<=M+1; i++ )// j <= M+1
28         {
29             int j = i + w;
30             int t = INT_MAX;
31             for(int k=i+1; k<j; k++){
32                 t = min( t, dp[i][k]+dp[k][j]);
33             }    
34             dp[i][j] = t + A[j] - A[i] - 2;
35         }    
36     } 
37     printf("%d\n",dp[0][M+1]);
38 }
39 
40 int main()
41 {
42     scanf("%d",&T);
43     while( T-- )
44     {
45         scanf("%d%d",&N,&M);
46         for(int i=1; i<=M; i++){
47             scanf("%d",&A[i]);
48         }
49         
50         solve();
51     }
52     
53     return 0;
54 }
相關文章
相關標籤/搜索