机试算法讲解: 第52题 动态规划之搬寝室问题

/*
动态规划题的思路:设定dp[i],dp[i][j]等数字量进行求解,这个数字量就称为:状态。
                状态:含义:是秒数当前状况的一个数字量。
				      特点:是数字,保存在内存中,可以表示一个状态的特征,不需要辅助信息。最重要:状态间的转移依赖于状态本身。
					  dp(dynamic plan,动态规划)
				关键:寻找一个好的状态
				状态转移:由一个或多个旧的状态得到一个新的状态的过程。例如:通过dp[i-1][j-1]推出dp[i][j]。
				转移规则(状态转移方程):数字量之间的递推关系。
				确定状态的转移规则:即确定怎样由前序状态递推求出后续状态。
				dp问题的复杂度估计:两个字符串的长度分别为L1和L2,共有L1*L2个状态要求解,为求解每个状态,按照相应字符是否相等选取dp[i-1][j-1]+1或者max{
				dp[i-1][j],dp[i][j-1]}为dp[i][j]的值,即每个状态的得出需要O(1)的时间,所以总的时间复杂度为O(L1*L2*1)
				对于最长递增子序列:原数列长度为n,则状态数量为dp[n],状态转移过程中每个状态得出的复杂度为O(n),总时间复杂度为O(n*n)

				dp时间复杂度=状态数量*状态转移复杂度

问题:搬寝室。n件物品,n<2000.准备搬2*k(<=n)件物品。每搬一次的疲劳度和左右手之间的重量差的平方成正比。请求出办完这2*k件物品后的最低疲劳度是多少
输入:每组输入数据有2行,第一行有2个数n,k(2<=2*k<=n<2000),第二行有n个整数,分别表示n件物品的重量(<2的15次方)
输出:对应每组输入数据,输出数据只有一个表示他的最少的疲劳度,每个一行.
输入:
2 1
1 3
输出: 
4

思路:设a<=b<=c<=d经计算ab,cd配对方案的累计疲劳度<=ac,bd配对方案的累计疲劳度。结论:每一对组合的两个物品重量,是原物品中重量相邻的两个物品。
     先对物品排序。设dp[i][j]表示前j件物品中选择i对物品的最小疲劳度。状态来源:
	 1)物品j和j-1未配对,则物品j每被选择,dp[i][j]=dp[i][j-1]
	 2) 物品j和j-1配对,等价于配对i-1对,将j-1与j进行配对dp[i][j]=dp[i-1][j-2] + (list[j]-list[j-1])的平方
关键:
1 状态转移方程dp[i][j]= min{dp[i][j-1] ,dp[i-1][j-2] + (list[j]-list[j-1])的平方},其中dp[0][n]=0
2 dp时间复杂度=状态数量*状态转移复杂度=(k*n)*O(1)=O(k*n)
3 你要求出的值是dp[k][n]
4 注意两边的i,j需要配对。i表征k,j表征n,j=2*i
5 程序还没有运行就退出,说明你声明了一个全局变量,而且申请的空间很大,导致崩溃
6 程序发生错误,粗心:scanf("%d",&Arr[n]);dp[0][n] = 0;应该把n改成i。
7 本题技巧,j > 2*i dp[i][j]=dp[i-1][j],j<=2*i,dp[i][j]=INT_MAX,比较dp[i][j]和dp[i-1][j-2]的大小
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#define INT_MAX 123123123
#define NN 201

using namespace std;

int partition(int* Arr,int low,int high)
{
	int iPos = Arr[low];
	while(low < high)
	{
		while(low < high && Arr[high] >= iPos)
		{
			high--;
		}
		Arr[low] = Arr[high];
		while(low < high && Arr[low] <= iPos)
		{
			low++;
		}
		Arr[high] = Arr[low];
	}
	Arr[low] = iPos;
	return low;
}

void quickSort(int* Arr,int low,int high)
{
	if(low < high)
	{
		int iPos = partition(Arr,low,high);
		quickSort(Arr,low,iPos-1);
		quickSort(Arr,iPos+1,high);
	}
}

int main(int argc,char* argv[])
{
	int n,k;
	int dp[NN][NN];
	int Arr[NN];
	int i,j;
	while(EOF!=scanf("%d %d",&n,&k))
	{
		//对状态数组进行初始化
		for(i = 0; i <= n ; i++)
		{
			dp[0][i] = 0;
		}

		//接受输入信息
		//for(i = 0; i < n ; i++)
		//易错,这里的i与j要与下面的进行比对质量时配对
		for(i = 1 ; i <= n ; i++)
		{
			scanf("%d",&Arr[i]);
		}

		//对重量进行快速排序
		quickSort(Arr,1,n);
		//sort(Arr,Arr+1,Arr+1+n);

		//开始进行动态规划
		for(i = 1 ; i <= k ; i++)
		{
			//易错,j必须从2i开始
			//for(j = 2 ; j <=n ; j++)
			for(j=2*i ; j <= n ; j++)
			{
				//如果j > 2*i表示,最后2个物品可以不配对
				if(j > 2*i)
				{
					dp[i][j] = dp[i-1][j];
				}
				//j<=2*i,最后2个物品必须配对
				else
				{
					dp[i][j]=INT_MAX;//否则前j件物品配不成i对,所以其状态不能由dp[i][j-1]转移而来,dp[i][j]先设置为正无穷大
				}
				//判定是dp[i-1][j-2]的值与其他的值比较,更新这个值
				if(dp[i][j] > dp[i-1][j-2] + (Arr[j]-Arr[j-1])*(Arr[j]-Arr[j-1]))
				{
					dp[i][j] = dp[i-1][j-2] + (Arr[j]-Arr[j-1])*(Arr[j]-Arr[j-1]);
				}
			}
		}
		//最终你所要求的是dp[k][n]
		printf("%d\n",dp[k][n]);

	}
	system("pause");
	getchar();
	return 0;
}

你可能感兴趣的:(动态规划,机试算法)