二维DP hdu 1421 搬寝室问题
题目链接 ---------->http://acm.hdu.edu.cn/showproblem.php?pid=1421<-------------
题目分析:
有n样东西,需要搬k次,每次搬两个,搬2*k个 ,求最少力气。很明显绝对值差值越小越好,于是读入n个物品质量后排序,每一个物品只有可能与前一个物品或后一个物品一起搬,绝对值才是可能当前最小。于是有DP思路,二维DP 数组 dp[i][j] 表示,到达选择第i个物品时,已经选了j对成功的物品的最小力气。当到达第i个物品时,可以选择是否选择搬动该物品 。所以有转移方程 dp[i][j] = min(dp[i - 2][j - 1] + (a[i - 1] - a[i - 2])^2,dp[i - 1][j]); 当然选择第i个物品时,可以规定j的范围,2*j <= i 已经选过的总物品数至少为选中对数两倍。由于这里取小值,全初始为0并不好,应当初始为一个极大值INF = 0x3f3f3f3f; 举个例子 当选过第六个物品,前面成功选了3对时: dp[6][3] = min(dp[4][2] + (a[5] - a[4])^2 , dp[5][3]); 然而我们明白dp[5][3] 在前面并不会循环到,因为有限制条件 2*j <= i; 如果全初始化为0,那么这个选取最小值就会出错(为0)所以我们将其dp全初始化为最大值。然后只有 dp[i][0] = 0 (到达任何一个物品,前面选了0对,最小值为0)。最后 输出选到n个物品,选了k对的值就可以。也就是dp[n][k];
代码君:
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <string.h> 5 6 using namespace std; 7 #define MAX 2010 8 #define M (a[i - 1] - a[i - 2]) 9 #define INF 0x3f3f3f3f 10 11 int dp[MAX][MAX]; 12 int a[MAX]; 13 int min(int a , int b) {return a < b ? a : b ;} 14 int n , k; 15 void init() 16 { 17 memset(dp,INF,sizeof(dp)); 18 for(int i = 0 ; i <= n; i++) dp[i][0] = 0; 19 return ; 20 } 21 22 23 int main() 24 { 25 while(cin >> n >> k) 26 { 27 init(); 28 29 for(int i = 0; i < n ; i++) cin >> a[i]; 30 sort(a , a + n); 31 init(); 32 33 for(int i = 1 ; i <= n ; i++) 34 { 35 for(int j = 1 ; 2 * j <= i && j <= k ; j++) 36 { 37 dp[i][j] = min(dp[i - 2][j - 1] + M * M , dp[i - 1][j]); 38 } 39 } 40 41 cout << dp[n][k] << endl; 42 } 43 return 0; 44 }