T1:奇怪的银行

可以直接把 1,6p,9p 当做物品大小,跑一遍完全背包。时间复杂度为 O(nlogn)

dp[i][j] 表示前 i 种面值恰好凑出 j 元的最少张数

转移:

dp[i][j]=min(dp[i1][j],dp[i][jwi]+1)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
inline void chmin(int& x, int y) { if (x > y) x = y; }
int main() {
int m;
cin >> m;
vector<int> w(1, 1);
for (int i = 6; i <= m; i *= 6) w.push_back(i);
for (int i = 9; i <= m; i *= 9) w.push_back(i);
int n = w.size();
const int INF = 1001001001;
vector<int> dp(m+1, INF);
dp[0] = 0;
rep(i, n) {
for (int j = w[i]; j <= m; ++j) {
chmin(dp[j], dp[j-w[i]]+1);
}
}
cout << dp[m] << '\n';
return 0;
}

T2:用户名已被使用

本题难度较大,首先枚举 n 个字符串的全排列,然后枚举这些字符串之间下划线的个数。

假设 sisi+1 之间有 xi 个下划线,那么 x1+x2+++xn115|s|15n

枚举出全排列之后,写一个 dfs 枚举每个 xi 的值,拼出用户名,然后判断是否已被占用,并将没被占用的用户名存入答案数组。最后将答案数组排序后输出。所有可能的用户名最多可能有 n!C15nn1,当 n=7 时,这个值取到最大值 141120。答案数组的大小要比这个数大。

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<string> S(n);
int len = 0;
rep(i, n) {
cin >> S[i];
len += S[i].size();
}
sort(S.begin(), S.end());
set<string> ts;
rep(i, m) {
string t;
cin >> t;
ts.insert(t);
}
vector<string> ans;
// cnt 表示已经用了多少个_
auto dfs = [&](auto f, int step, int cnt, string name) -> void {
if (step == n-1) {
name += S.back();
if (name.size() >= 3 and name.size() <= 15 and !ts.count(name)) {
ts.insert(name);
ans.push_back(name);
}
return;
}
for (int i = 15-len-cnt; i >= 1; --i) {
string ul(i, '_');
f(f, step+1, cnt+i, name+S[step]+ul);
}
};
do {
dfs(dfs, 0, 0, "");
} while (next_permutation(S.begin(), S.end()));
sort(ans.begin(), ans.end());
if (!ans.size()) {
puts("-1");
return 0;
}
for (string s : ans) cout << s << '\n';
return 0;
}