动态规划实例

#include<iostream>
#include<Windows.h>
#include<assert.h>
#include <algorithm>
#include <functional>
#include<vector>
#include<math.h>
#include<unordered_map>
using namespace std;

//买书问题
#if 0
int main(void)
{
	double discount[6] = { 0, 1, 1.9, 2.7, 3.2, 3.75 };//存放购买不同本数的折扣
	int book[6] = { 0 };
	int bookCount = 0;//书本总类目
	int bookPrice = 8;//书本价格
	cout << "请输入五类书中每一类书的数目:" << endl;
	for (int i = 1; i <= 5; i++)
	{
		cin >> book[i];//输入各类书本的数目
	}
	sort(book + 1, book + sizeof(book) / sizeof(int), greater<int>());//保证Y1>=Y2>=Y3>=Y4>=Y5
	float arr[6][6][6][6][6] = { 0 };//存放折扣计算过程
	float minDis[6][6][6][6][6] = { 0 };//存放享有的最大折扣


	int y1 = 0;
	int y2 = 0;
	int y3 = 0;
	int y4 = 0;
	int y5 = 0;
	/*
	实现公式F(Y1,Y2,Y3,Y4,Y5) = MIN{...}  时间复杂度O(Y1×Y2×Y3×Y4×Y5) 空间复杂度为(Y1×Y2×Y3×Y4×Y5)
	*/
	for (y5 = 0; y5 <= book[5]; y5++)
	{
		for (y4 = y5; y4 <= book[4]; y4++)
		{
			for (y3 = y4; y3 <= book[3]; y3++)
			{
				for (y2 = y3; y2 <= book[2]; y2++)
				{
					for (y1 = y2; y1 <= book[1]; y1++)
					{
						double a[6] = { 0 };//存放不同购书策略所产生的临时数据
						int dir[5] = { 0 };//存放下一步策略的Y1 Y2 Y3 Y4 Y5
						if (y5 > 0)
						{
							dir[4] = y5 - 1;
							dir[3] = y4 - 1;
							dir[2] = y3 - 1;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());//保证Y1>=Y2>=Y3>=Y4>=Y5
							a[5] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[5] * bookPrice;
						}
						if (y4 > 0)
						{
							dir[4] = y5;
							dir[3] = y4 - 1;
							dir[2] = y3 - 1;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[4] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[4] * bookPrice;
						}
						if (y3 > 0)
						{
							dir[4] = y5;
							dir[3] = y4;
							dir[2] = y3 - 1;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[3] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[3] * bookPrice;
						}

						if (y2 > 0)
						{
							dir[4] = y5;
							dir[3] = y4;
							dir[2] = y3;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[2] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[2] * bookPrice;
						}
						if (y1 > 0)
						{
							dir[4] = y5;
							dir[3] = y4;
							dir[2] = y3;
							dir[1] = y2;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[1] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[1] * bookPrice;
						}
						for (int i = 0; i < sizeof(a) / sizeof(double); i++)
						{
							if ((minDis[y1][y2][y3][y4][y5] > a[i] && a[i]) || minDis[y1][y2][y3][y4][y5] == 0)
								minDis[y1][y2][y3][y4][y5] = a[i];//将折扣最大的(即数字最小,但不为0的元素记录在minDIs中)
						}
						arr[y1][y2][y3][y4][y5] = minDis[y1][y2][y3][y4][y5];//将minDis赋值给arr[y1][y2][y3][y4][y5],下一轮继续使用
					}
				}
			}
		}
	}
	printf("当前购买书本可享有最低的价格为:%.2f", minDis[book[1]][book[2]][book[3]][book[4]][book[5]]);//书本享有的总优惠*书本价格*书本数目/书本数目=书本享有总优惠*书本价格

	cout << endl;
	system("pause");
	return 0;
}

double Min(double a, double b, double c, double d, double e)
{
	double n[5] = { a, b, c, d, e };
	double minelement = 1000.0;
	for (int i = 0; i < 5; i++)
	{
		if (n[i] < minelement) minelement = n[i];
	}
	return minelement;
}

double Solve(int x1, int x2, int x3, int x4, int x5)
{
	int n[5] = { x1, x2, x3, x4, x5 };
	sort(n, n + 5, greater<int>());
	x1 = n[0];
	x2 = n[1];
	x3 = n[2];
	x4 = n[3];
	x5 = n[4];
	if (x5 > 0)
	{
		return Min(8.0 + Solve(x1 - 1, x2, x3, x4, x5),
			2 * 8 * 0.95 + Solve(x1 - 1, x2 - 1, x3, x4, x5),
			3 * 8 * 0.9 + Solve(x1 - 1, x2 - 1, x3 - 1, x4, x5),
			4 * 8 * 0.8 + Solve(x1 - 1, x2 - 1, x3 - 1, x4 - 1, x5),
			5 * 8 * 0.75 + Solve(x1 - 1, x2 - 1, x3 - 1, x4 - 1, x5 - 1));
	}
	else if (x5 == 0 && x4 > 0)
	{
		return Min(8.0 + Solve(x1 - 1, x2, x3, x4, x5),
			2 * 8 * 0.95 + Solve(x1 - 1, x2 - 1, x3, x4, x5),
			3 * 8 * 0.9 + Solve(x1 - 1, x2 - 1, x3 - 1, x4, x5),
			4 * 8 * 0.8 + Solve(x1 - 1, x2 - 1, x3 - 1, x4 - 1, x5), INT_MAX);
	}
	else if (x5 == 0 && x4 == 0 && x3 > 0)
	{
		return Min(8.0 + Solve(x1 - 1, x2, x3, x4, x5),
			2 * 8 * 0.95 + Solve(x1 - 1, x2 - 1, x3, x4, x5),
			3 * 8 * 0.9 + Solve(x1 - 1, x2 - 1, x3 - 1, x4, x5),
			INT_MAX, INT_MAX);
	}
	else if (x5 == 0 && x4 == 0 && x3 == 0 && x2 > 0)
	{
		return Min(8.0 + Solve(x1 - 1, x2, x3, x4, x5),
			2 * 8 * 0.95 + Solve(x1 - 1, x2 - 1, x3, x4, x5),
			INT_MAX, INT_MAX, INT_MAX);
	}
	else if (x5 == 0 && x4 == 0 && x3 == 0 && x2 == 0 && x1 > 0)
	{
		return 8.0*x1;
	}
	else return 0;
}

int  main2()
{
	double solution = Solve(2, 2, 2, 1, 1);
	cout << solution << endl;
	return 0;
}
#endif

//快速找出故障机器
#if 0
class Solution
{
public:
	//一台机器故障
	int reslove1(vector<int>& nums)
	{
		if (nums.empty())
		{
			return -1;
		}
		unordered_map<int, int> hash_table;

		for (int i = 0; i < nums.size(); i++)
		{
			++hash_table[nums[i]];
		}
		for (int i = 0; i < nums.size(); i++)
		{
			if (hash_table[nums[i]] < 2)
				return nums[i];
		}
		return 0;
	}
	int reslove2(vector<int>& nums)
	{
		if (nums.empty())
		{
			return -1;
		}

		int result = 0;
		for (int i = 0; i < nums.size(); i++)
		{
			result = result ^ nums[i];
		}
		return result;
	}

};

int main()
{
	Solution solution;
	vector<int> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 8, 9 };
	int result1 = solution.reslove1(nums);
	int result2 = solution.reslove2(nums);
	cout << result1 << ";"<< result2 << endl;

	return 0;
}
#endif

//饮料供货问题
#if 0
#define INF 0x7fffffff  
const int N = 3;//饮料种数      
const int V = 8;//水房容量      

int C[N + 1] = { 0, 8, 4, 2 };  //Ci表示第i种饮料的容量   (i从1开始)      
int H[N + 1] = { 0, 2, 3, 1 };   //H[i]表示第i种饮料的满意度   

int dp[N + 1][V + 1];      ///总满意度

//备忘录法
int Cal(int n, int v){
	if (n > N){
		if (v == 0)
			return 0;
		else 
			return -INF;
	}

	if (v<0)
		return -INF;
	else if (v == 0)
		return 0;
	else if (dp[n][v] != -1)
		return dp[n][v];

	int ret = -INF;
	for (int i = 0; i <= V / C[n]; i++){
		int temp = Cal(n + 1, v - i*C[n]);
		if (temp != -INF){
			temp += H[n] * i;
			ret = max(ret, temp);
		}
	}
	return dp[n][v] = ret;
}
int main(){

	int V = 10;
	for (int i = 1; i <= N; i++)
	for (int j = 1; j <= V; j++)
		dp[i][j] = -1;
	
	cout << Cal(1, V) << endl;
	return 0;
}

#endif
#if 0
/*
注意到这个题目的限制,每种饮料容量只能是2的方幂

1)对于总容量V,假设为10,写成2进制形式为,1010,因为每种容量只能是2的方幂,所以对于容量10,也就是等于,
拿8容量的饮料最大值加上拿2容量得饮料的最大值

2)对于相等容量的饮料,当然只用取满意度最高的那种就行了
*/
#define INF 0x7fffffff  
const int N = 4;//饮料种数      
const int V = 7;//水房容量      

struct node{
	int C;
	int H;
	bool operator<(const node &t)const{
		return H<t.H;
	}
};
node water[N + 1] = { { 0, 0 }, { 8, 4 }, { 4, 3 }, { 4, 2 }, { 2, 1 } };

node *bucket[20][20] = { NULL };
int length[20] = { 0 };
int main(){
	//将饮料按照容量进行分类,假设最多20类,如容量为2=2^1,所以将它放到1位置  
	for (int i = 1; i <= N; i++){
		int cnt = 0;
		int t = water[i].C;
		while (!(t & 1)){
			t >>= 1; 
			cnt++;
		}
		bucket[cnt][length[cnt]++] = &water[i];
	}
	//每个类别按照满意度进行排序,选出满意度最高的此类饮料  
	for (int i = 0; i<20; i++){
		if (length[i] == 0)continue;

		sort(bucket[i], bucket[i] + length[i]);
	}


	//store[i]数组存储如要拿2^i容量的饮料所获得的最大满意度  
	int store[20];
	memset(store, -1, sizeof(store));

	store[0] = length[0]>0 ? bucket[0][0]->H : -1;
	for (int i = 1; i<20; i++){
		int t = -1;
		if (length[i]>0)t = bucket[i][0]->H;
		store[i] = max(t, 2 * store[i - 1]);
		if (store[i]>V)break;
	}

	//将V转化成2进制数据求总满意度  
	int H = 0;//总满意度  
	int C = 0;//总容量  
	int v = V;
	int k = 0;
	while (v>0){
		if (v & 1){
			if (store[k]<0){
				cout << "无法组成" << (1 << k) << "容量的饮料" << endl;
				//break;  
			}
			else {
				H += store[k];
				C += 1 << k;
			}
		}
		v >>= 1;
		k++;
	}

	cout << "总容量为" << V << endl << "所用容量为" << C << endl << "最大满意度为" << H << endl;
	return 0;
}

#endif
#if 0
/**
* W:饮料总容量的最大上限;N:饮料的种类;C:每种饮料的个数;H:每种饮料的满意度;V:每种饮料的容量
* 求:在满足最大容量<=W的前提下,如何购买饮料获得的满意度最大
*/
int Cal(int W, int N, int* C, int* H, int* V)
{
	if (W <= 0 || N <= 0 || C == NULL || H == NULL || V == NULL)return 0;
	int i, j, k;
	int** dp = new int*[N + 1];
	for (i = 0; i <= N; i++)
	{
		dp[i] = new int[W + 1];
	}
	memset(dp[N], 0, sizeof(int)*(W + 1));
	for (i = N - 1; i >= 0; i--)
	{
		for (j = 1; j <= W; j++)
		{
			dp[i][j] = 0;
			for (k = 0; k <= C[i]; k++)
			{
				if (j >= k*V[i])
				{
					dp[i][j] = max(dp[i][j], dp[i + 1][j - k*V[i]] + k*H[i]);
				}
				else break;
			}
		}
	}
	int res = dp[0][W];
	for (i = 0; i <= N; i++)delete[] dp[i];
	delete[] dp;
	return res;
}

int main()
{
	int w, n, i;
	while (cin >> w >> n)
	{
		int* C = new int[n];
		int* H = new int[n];
		int* V = new int[n];
		for (i = 0; i<n; i++)
			cin >> C[i] >> H[i] >> V[i];
		cout << Cal(w, n, C, H, V) << endl;
		delete[] C;
		delete[] H;
		delete[] V;
	}
	return 0;

}



#endif


//0-1背包问题:每个物品只能拿一个或者不拿
#if 0
const int N = 4; //物品数量  
const int C = 7; //背包容量  
int w[N] = { 2, 3, 5, 2 };   //w[i]表示第i个物品的容量
int v[N] = { 6, 4, 8, 4 };   //v[i]表示第i个物品的价值
int m[N][C + 1]; //记录子问题的解的表格  
int x[N];

void initial()
{
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j <= C; j++)
		{
			m[i][j] = -1;
		}
	}
}

//以下使用备忘录法求解0-1背包问题(物品重量为整型)  
int KnapsackMemoMethod(int i, int capacity)
{
	if (m[i][capacity] != -1)
		return m[i][capacity]; //使用m[i][capacity]需要检查两个下标是否出界  
	int result = 0;
	if (i == N - 1)
	{
		if (capacity >= w[i])
		{
			m[i][capacity] = v[i];
			return v[i];
		}
		else
		{
			m[i][capacity] = 0;
			return 0;
		}
	}
	else
	{
		if (capacity >= w[i])
		{
			int selected = KnapsackMemoMethod(i + 1, capacity - w[i]) + v[i];
			int unselected = KnapsackMemoMethod(i + 1, capacity);
			result = selected > unselected ? selected : unselected;
			m[i][capacity] = result;
			return result;
		}
		else //capacity < w[i]  
		{
			result = KnapsackMemoMethod(i + 1, capacity);
			m[i][capacity] = result;
			return result;
		}

	}
}

void Trace(int i, int capacity)
{
	if (i == N - 1)
	{
		if (m[i][capacity] == v[i])
			x[i] = 1;
		else
		{
			x[i] = 0;
		}
		return;
	}
	else
	{
		if (m[i][capacity] == m[i + 1][capacity])
		{
			x[i] = 0;
			Trace(i + 1, capacity);
		}
		else
		{
			x[i] = 1;
			Trace(i + 1, capacity - w[i]);
		}
	}
}

int main()
{
	initial();
	cout << KnapsackMemoMethod(0, C) << endl;
	Trace(0, C);
	for (int i = 0; i < N; i++)
	{
		cout << x[i] << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}
#endif

#if 0
const int N = 4; //物品数量  
const int C = 9; //背包容量  
int w[N+1] = {0, 2, 3, 5, 2 };   //w[i]表示第i个物品的容量
int v[N+1] = {0, 6, 4, 8, 4 };   //v[i]表示第i个物品的价值
int m[N+1][C + 1]; //记录子问题的解的表格  

int DP_BG(int N,int C)
{
	for (int i = 0; i <= N; i++)
	{
		for (int j = 0; j <= C; j++)
		{
			m[i][j] = 0;
		}
	}

	for (int i = 1; i <= N; i++)
	{
		for (int j = 0; j <= C; j++)
		{
			if (j >= w[i])
			{
				m[i][j] = max(m[i-1][j], m[i - 1][j - w[i]] + v[i]);
			}
			else
			{
				m[i][j] = m[i-1][j];
			}
		}

	}
	return m[N][C];
}

int x[N + 1];  //存储最优解的物品选取情况,1表示选取,0表示不选

void trace(int i, int j)
{
	if (i < 0)  return;
	else
	{
		if (m[i][j] == m[i - 1][j])
		{
			x[i] = 0;
			trace(i - 1, j);
		}
		else /*if ( j - w[i] >= 0 && m[i][j] = m[i - 1][j] + v[i])*/
		{
			x[i] = 1;
			trace(i - 1, j - w[i]);
		}

	}


}
int main()
{

	int result = DP_BG(N,C);
	trace(N, C);
	cout << result<< endl;
	for (int i = 1; i <= N; i++)
		cout << x[i] << " ";
	cout << endl;
	return 0;
}
#endif


//延伸
#if 0

const int N = 5;  //五个物品
const int M = 900;  //总共钱数

int v[N + 1] = {0, 300, 500, 600, 400,200 };  //每件物品的价格
int p[N + 1] = { 0, 9, 5, 7, 4 ,3};   //每件物品的价值程度
int maxValue[N+1][M+1];    //最大价值


int DP(int N, int M)
{
	for (int i = 0; i <= N; i++)     //首先初始化
	{
		for (int j = 0; j <= M; j++)
		{
			maxValue[i][j] = 0;    
		}
	}

	for (int i = 1; i <= N; i++)     
	{
		for (int j = 0; j <= M; j++)
		{
			if (j >= v[i])
				maxValue[i][j] = max(maxValue[i - 1][j], maxValue[i - 1][j - v[i]] + p[i]);
			else
				maxValue[i][j] = maxValue[i - 1][j];
		}
	}
	return maxValue[N][M];
}
int x[N+1];
void findWhat(int N, int M)
{
	if (N <= 0)  return;
	else
	{
		if (maxValue[N][M] == maxValue[N-1][M])
		{
			x[N] = 0;
			findWhat(N - 1, M);
		}
		else
		{
			x[N] = 1;
			findWhat(N - 1, M - v[N]);
		}
	}

}
int main()
{
	int result = 0;
	result = DP(N, M);
	cout << result << endl;
	findWhat(N,M);
	for (int i = 1; i <= N; i++)
	{
		cout << x[i] << " ";
	}
	cout<<endl;
	
}
#endif
//硬币问题
#if 0
#define INF 100000000
#define MAXNUM 10000
#define MONEYKIND 100

int n, S;
int V[MONEYKIND];
int MIN[MAXNUM], MAX[MAXNUM];

void dp(int*, int*);
//递推方法函数声明
void print_ans(int*, int);
//输出函数声明

int main()
{
	int i;
	printf("输入硬币的种数n(1-100):\n");
	scanf("%d", &n);
	printf("输入要组合的钱数S(0-10000):\n");
	scanf("%d", &S);
	printf("输入硬币种类:\n");
	for (i = 1; i <= n; i++)
	{
		scanf("%d", &V[i]);
	}
	dp(MIN, MAX);
	printf("最小组合方案:\n");
	print_ans(MIN, S);
	printf("\n");
	printf("最大组合方案:\n");
	print_ans(MAX, S);

	return 0;
}

void dp(int *min, int *max)
//递推过程实现
{
	int i, j;
	min[0] = max[0] = 0;
	for (i = 1; i <= S; i++)//初始化数组
	{
		min[i] = INF;
		max[i] = -INF;
	}
	for (i = 1; i <= S; i++)
	for (j = 1; j <= n; j++)
	if (i >= V[j])
	{
		if (min[i - V[j]] + 1<min[i]){
			min[i] = min[i - V[j]] + 1;//最小组合过程
		}
		if (max[i - V[j]] + 1>max[i])
			max[i] = max[i - V[j]] + 1;//最大组合过程
	}
	cout << *min_element(max + 1, max + S + 1) << endl;  //最小组合的硬币数量
	cout << *max_element(max + 1, max + S + 1) << endl;  //最大组合的硬币数量
}

void print_ans(int *d, int S)
//输出函数实现
{
	int i;
	for (i = 1; i <= n; i++)
	if (S >= V[i] && d[S] == d[S - V[i]] + 1)
	{
		cout << V[i] << " ";
		print_ans(d, S - V[i]);
		break;
	}
}
#endif

#if 0
const int N = 4;
const int C = 7;
int w[N + 1] = {0,2,3,5,2};
int v[N + 1] = {0,6,4,8,4};

int m[N + 1][C + 1];//记录子问题
int x[N + 1];
void initial()
{
	for (int i = 0; i <= N; i++)
	{
		for (int j = 0; j <= C; j++)
		{
			m[i][j] = -1;
		}

	}
}
int KnapsackMemoMethod(int i, int capacity)
{
	if (m[i][capacity] != -1) return m[i][capacity];

	if (i == N)
	{
		if (capacity >= w[i])
		{
			m[i][capacity] = v[i];
			return v[i];
		}
		else
		{
			m[i][capacity] = 0;
			return 0;
		}

	}
	else
	{
		if (capacity >= w[i])
		{
			int selected = v[i] + KnapsackMemoMethod(i+1,capacity - w[i]);
			int unselected = KnapsackMemoMethod(i + 1, capacity);
			m[i][capacity] = max(selected,unselected);
			return m[i][capacity];
		}
		else
		{
			m[i][capacity] = KnapsackMemoMethod(i + 1, capacity);
			return m[i][capacity];
		}
	}

}

void Trace(int i, int capacity)
{
	if (i == N)
	{
		if (m[i][capacity] == v[i])
			x[i] = 1;
		else
			x[i] = 0;
	}
	else
	{
		if (m[i][capacity] == m[i + 1][capacity])
		{
			x[i] = 0;
			Trace(i + 1, capacity);
		}
		else
		{
			x[i] = 1;
			Trace(i + 1, capacity - w[i]);
		}
			
	}

}

int main()
{
	initial();
	cout << KnapsackMemoMethod(1, C) << endl;
	Trace(1, C);
	for (int i = 1; i <= N; i++)
	{
		cout << x[i] << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}


#endif

//导弹拦截问题
#if 0
void intercept()
{
	vector<int> missile = { 389, 207, 155, 300, 299, 170, 158, 65 };
	int len = missile.size();
	vector<int> note(len);
	for (int i = 0; i < len; i++)
		note[i] = 0;//note表初始化

	for (int i = len - 1; i >= 0; i--)//从最后一个导弹开始向前分析
	{
		int max = 0, flag = i;//max表示向后查询记录表note时,当前查询到的最多可拦截导弹数量,flag表示当前查到的最大数量导弹的下标
		cout << "i=" << i << endl;
		for (int j = i + 1; j < len; j++)
		{
			if (missile[i] > missile[j] && note[j] > max)//当当前导弹高度大于之后第j个导弹高度,且第j个导弹之后可拦截最多数量大于目前已知最大数量时候,更新flag和max;
			{
				flag = j;
				max = note[j];
			}
		}
		note[i] = note[flag] + 1;
		for (auto x : note)
			cout << x << " ";
		cout << endl;
	}

	return;
}

int main()
{

	intercept();
	return 0;
}
#endif
//切钢条问题
#if 0
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;

int main()
{
	vector<int> table = { 0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30 };//方便理解,数组下标从1开始,0补位 C++11新标准vector初始化方式
	int n;
	cin >> n;//输入钢条长度
	vector<int> r(n + 1, 0);//方便理解,数组下标从1开始,初始化11位数组
	for (int i = 1; i <= n; i++)
	{
		int max = -1;
		for (int j = 1; j <= i; j++)
		{
			if (max < table[j] + r[i - j])
				max = table[j] + r[i - j];
		}
		r[i] = max;
	}
	for (auto x : r)
		cout << x << " ";
	cout << endl;
	return 0;
}

#endif





猜你喜欢

转载自blog.csdn.net/sinat_36412790/article/details/80416722