Atcoder Beginner Contest 118D - Match Matching 解题报告
1 题目链接
2 题目大意
题目:火柴匹配
题目大意:
问恰好 n n n 根火柴可以拼出最大的数是多少。
3 解法分析
十年OI一场空,不用string见祖宗
看到这道题后首先考虑贪心。
诶不对,再看看第一个样例。
这不符合贪心罢?
不考虑贪心了,直接 d p dp dp (恼)。
等等先别急,考虑一下更朴素的方法。
考虑 O ( 2 n ) O(2^n) O(2n) 的 d f s dfs dfs。
诶 n ⩽ 1 0 4 n\leqslant10^4 n⩽104,那没事了。还是 d p dp dp 罢。
由样例可知每种数字可以用无数遍。
这不裸完全背包么(狂喜)。
状态: d p i dp_i dpi 表示用 i i i 根火柴棍能获得的最大数。
易知转移方程:
d p i = max { cal ( d p i − c a i , a i ) , d p i } dp_i=\max\{\operatorname{cal}(dp_{i-c_{a_i}},a_i),dp_i\} dpi=max{cal(dpi−cai,ai),dpi}
其中 cal ( i , j ) \operatorname{cal}(i,j) cal(i,j) 表示向 i i i 中加入数字 j j j。
完结撒花。
4 解法总结
完全背包。
状态: d p i dp_i dpi 表示用 i i i 根火柴棍能获得的最大数。
转移方程:
d p i = max { cal ( d p i − c a i , a i ) , d p i } dp_i=\max\{\operatorname{cal}(dp_{i-c_{a_i}},a_i),dp_i\} dpi=max{cal(dpi−cai,ai),dpi}
其中 cal ( i , j ) \operatorname{cal}(i,j) cal(i,j) 表示向 i i i 中加入数字 j j j。
5 AC Code
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[17], c[17] = {0, 2, 5, 5, 4, 5, 6, 3, 7, 6};
struct node {
int cnt[17], len;
bool operator > (const node& b) const {
if (len != b.len)
return len > b.len;
for (int i = m; i >= 1; --i)
if (cnt[i] != b.cnt[i])
return cnt[i] > b.cnt[i];
return 0;
}
}dp[10007];
int main() {
scanf("%d%d", &n ,&m);
for (int i = 1; i <= m; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + m + 1);
for (int i = 1; i < n; ++i)
dp[i].len = -1000007;
for (int i = m; i >= 1; --i) {
for (int j = 0; j <= n; ++j) {
if (c[a[i]] <= j) {
node t = dp[j - c[a[i]]];
++t.cnt[i];
++t.len;
if (t > dp[j])
dp[j] = t;
}
}
}
for (int i = m; i >= 1; --i)
for (int j = 1; j <= dp[n].cnt[i]; ++j)
putchar(a[i] + '0');
puts("");
return 0;
}