现在的位置: 首页 > 综合 > 正文

【PAT Advanced Level】1068. Find More Coins (30)

2017年12月15日 ⁄ 综合 ⁄ 共 1209字 ⁄ 字号 评论关闭

最近在学习动态规划,PAT中的这一题就是一个典型的dp问题。

这一题和01背包问题很类似,M相当于背包问题中的背包容量,硬币面值相当于每件物品的重量,背包问题中要求物品价值最大,这里要求物品总重(面值和)等于M,从可选方案中选择最优的是根据给定的比较方法。

我们解决问题的难点:如何选择出最小的序列?

首先来看递归式:L(i, j)表示在前i号硬币中选择,并且总价值小于等于j的序列的最大面值和。这里我们不要求等于j,只要尽量接近j就可以了。a[i]是i号硬币的面值,则递归式如下:

L(i, j)   =   0
i == 0 || j == 0

  
   =    L[i - 1, j]
a[i] > j

  
    =    max(L(i - 1, j - a[i]) + a[i], L(i - 1, j))
a[i] <= j

自底向上填表之后,回溯一遍就可以得到所得序列。

但是想要保证序列“最小”,就必须将原面值递减排序。在回溯中每次遇到L(i - 1, j - a[i]) + a[i]这种情况,就决定选择a[i],因为此时a[i]总是最小的。

//类似于01背包的DP
//降序排列是关键!!!

#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
using namespace std;

int N, M;
vector<int> v;
vector<vector<int>> l;

void dp()
{
	for(int i = 1; i <= N; i++)
	{
		for(int j = 1; j <= M; j++)
		{
			if(v[i] > j)
				l[i][j] = l[i - 1][j];
			else 
				l[i][j] = l[i - 1][j - v[i]] + v[i] > l[i - 1][j] ? l[i - 1][j - v[i]] + v[i] : l[i - 1][j];
		}
	}

	if(l[N][M] != M)
	{
		cout<<"No Solution"<<endl;
		return;
	}

	//回溯输出选取的序列
	int j = M;
	int i = N;
	//cout<<" ";
	while (j > 0)
	{
		if(l[i][j] == l[i - 1][j - v[i]] + v[i])
		{
			cout<<v[i];
			j -= v[i];
			i -= 1;
			if(j != 0) cout<<" ";
		}
		else
		{
			i -= 1;
		}
	}
	cout<<endl;
}

bool cmp(const int &a, const int &b)
{
	return a > b;
}

int main()
{
	//fstream cin("a.txt");

	cin>>N>>M;
	v.push_back(0);
	for(int i = 0; i < N; i++)
	{
		int tmp;
		cin>>tmp;
		v.push_back(tmp);
	}
	sort(v.begin() + 1, v.end(), cmp);
	for(int i = 0; i <= N; i++)
	{
		vector<int> tmp;
		for(int j = 0; j <= M; j++)
			tmp.push_back(0);
		l.push_back(tmp);
	}
	dp();
}

抱歉!评论已关闭.