蓝桥杯 试题 算法提高 宰羊 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 }

 

posted @ 2020-05-01 21:27  代码改变头发  阅读(452)  评论(0编辑  收藏  举报