假定一张信封最多贴5张邮票,求出最大连续邮资。
输入S(S<=10) 和若干有邮票组合(面值不超过100) , 选出连续邮资最大 一个组合。如果有多个并列,邮票组合中邮票张数应最多,如果还有并列,邮票从大到小后字典序应最小。
思路:暴力求解动态规划,遍历邮资。 对于邮票组合C, dp[i] 表示邮资i至少需要多少张来自于C的邮票才能组合起来。
推导式:dp[i] = min(dp[i-x]+1 , x∈C && x <= i ); 答案为i-1;
+1 值加上当前的一张邮票 。
注:要求连续邮资 所以邮票组合中必须有一张面值是1 , 这样dp[1] = 1,
//UVa 242
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int MAXN = 10 + 1, MAXS = MAXN, MAXC = MAXS * 100 + 100 + 4;
int S, N, DP[MAXC];
int readint() {
int x;
cin >> x;
return x;
}
struct StampSet {
vector<int> D;
int maxCover;
void output() {
for (auto e : D)
printf("%3d",e);
}
//返回一个对象
StampSet& input() {
D.clear();
maxCover = 0;
//每行n个面额
int n = readint();
//面额值
while (n--)
D.push_back(readint());
sort(D.begin(), D.end());
return* this;
}
void getMaxCover() {
int i = 0;
fill(DP,DP+MAXN, MAXC);
DP[i] = 0;
while (true) {
//i代表邮资
i++;
int ans = inf;
//D已经是升序序列 单个面值不能大于要组成的邮资
/**************************************************/
for (int j = 0; j < D.size() && D[j] <= i; j++)
ans = min(ans, DP[i - D[j]] + 1);
/**************************************************/
if (ans > S)
break;
else
DP[i] = ans;
}
//邮资i超过S的 所以要减1
maxCover = i - 1;
}
bool operator<(const StampSet& rhs) const {
if (maxCover != rhs.maxCover)
return maxCover > rhs.maxCover;
if (D.size() != rhs.D.size())
return D.size() < rhs.D.size();
for (int i = D.size() - 1; i >= 0; i--)
if (D[i] != rhs.D[i])
return D[i] < rhs.D[i];
return true;
}
};
StampSet C[MAXN];
int main()
{
while (cin >> S && S) {
N = readint();
for (int i = 0; i < N; i++)
C[i].input().getMaxCover();
struct StampSet* mss = min_element(C,C+N);
// cout << "mss类型" << typeid(mss).name() << endl;
printf("max coverage = %3d :",mss->maxCover);
mss->output();
puts("");
}
return 0;
}