蓝桥杯 试题 算法提高 宰羊 DP解决
问题描述
炫炫回了内蒙,肯定要吃羊肉啦,所有他家要宰羊吃。
炫炫家有N只羊,羊圈排成一排,标号1~N。炫炫每天吃掉一只羊(这食量!其实是放生啦),吃掉的羊的邻居会以为它被放生了,然后又会告诉他们的邻居,这样一直传播下去,除非某个邻居已经被“放生”了。每一天,所有知道某羊被“放生”了这个消息的羊都会很不满,如果不给他们巧克力的话,他们就会很造反,炫炫已经知道他要吃掉哪些羊,他可以任意安排吃的顺序,然后使巧克力的用量最小,请求出这个最小值。
炫炫家有N只羊,羊圈排成一排,标号1~N。炫炫每天吃掉一只羊(这食量!其实是放生啦),吃掉的羊的邻居会以为它被放生了,然后又会告诉他们的邻居,这样一直传播下去,除非某个邻居已经被“放生”了。每一天,所有知道某羊被“放生”了这个消息的羊都会很不满,如果不给他们巧克力的话,他们就会很造反,炫炫已经知道他要吃掉哪些羊,他可以任意安排吃的顺序,然后使巧克力的用量最小,请求出这个最小值。
输入格式
本题有多组数据,第一行为数据组数T。
对于每组数据
第一行:两个用空格隔开的整数:N和M,表示羊的数量和需要吃掉的数量
第二行:有M个数,表示要吃那些羊。
对于每组数据
第一行:两个用空格隔开的整数:N和M,表示羊的数量和需要吃掉的数量
第二行:有M个数,表示要吃那些羊。
输出格式
T行,为每组数据的答案。
样例输入
2
8 1
3
20 3
3 6 14
8 1
3
20 3
3 6 14
样例输出
7
35
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 }