ABC358

1|0E - Alphabet Tiles

https://atcoder.jp/contests/abc358/tasks/abc358_e

方案数DP。

先看摆花(五年前做过)。

fi,j 表示摆完前 i 种花,目前已经有了 j 盆花的方案数。

可以考虑先枚举当前摆第 i 种花,然后再枚举摆完第 i 种花之后,目前已经有了 j 盆花。

不难发现,这种情况下,第 i 种花有可能摆了 0,1,2,,min(ai,j) 盆。

那么就可以得到:

fi,j=(fi1,j+fi1,j1+...+fi1,jmin(ai,j))(f0,0=1)

接下来还能滚动优化,从大到小枚举即可,不过我就不接着优化了。

signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n, m; std::cin >> n >> m; std::vector<int> a(n); for (auto& x : a) {std::cin >> x;} std::vector<Z> dp(m + 1); dp[0] = 1; for (int i = 0; i < n; i++) { std::vector<Z> ndp(m + 1); for (int j = 0; j <= m; j++) { for (int k = 0; k <= a[i] and j + k <= m; k++) {//枚举这种花最多能放几盆 ndp[j + k] += dp[j];//习惯正推 } } dp = ndp; } std::cout << dp[m] << '\n'; return 0; }

再来看这道题,其实也是摆花(字母),但是第 i 种字母可以随便插入位置,(而不是像本题一样同一种只能放在同一个位置),所以方案数是前 i1 种花的方案数乘以能插入这第 i 种花的位置方案数,即 (nk)(nki)

signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int K; std::cin >> K; std::vector<Z> dp(K + 1); dp[0] = 1; for (int i = 0; i < 26; ++i) { int C; std::cin >> C; std::vector<Z> ndp(K + 1); for (int j = 0; j <= K; ++j) for (int k = 0; k <= C and j + k <= K; ++k) { ndp[j + k] += dp[j] * comb.C(j + k, k);//n = j + k } dp = ndp; } std::cout << std::accumulate(dp.begin() + 1, dp.end(), Z(0)) << '\n'; return 0; }

答案是 i=1Kdpi,因为他要求长度为 1K 之间的字符串的方案数。

2|0G - AtCoder Tour

https://atcoder.jp/contests/abc358/tasks/abc358_g

贪心简化DP

先用贪心简化题目:

  • 显然会停留在一个最大的(路径中的最大),然后不断重复停留。
  • 显然到达最大的(路径中的最大)的之前,路径中不会有环,也就是整个路径中不会有环。

然后设计状态 dpk,i,j 为第 k 步走到 (i,j) 格子所能覆盖到的最大数字和,转移即可。

signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int H, W, K; std::cin >> H >> W >> K; const int N = H * W, Final_k = std::min(K, N); int Si, Sj; std::cin >> Si >> Sj; --Si; --Sj; std::vector a(H, std::vector<int>(W)); for (auto& vec : a) for (auto& x : vec) {std::cin >> x;} std::vector dp(N + 1, std::vector(H + 1, std::vector<i64>(W + 1, -1))); dp[0][Si + 1][Sj + 1] = 0; for (int k = 0; k < Final_k; k++) for (int i = 0; i < H; i++) for (int j = 0; j < W; j++) {//这里第一重循环必须是K,要先把所有坐标的K - 1步处理出来才能保证答案正确 for (const auto&[dx, dy] : {std::make_pair(0, 1), {0, -1}, {1, 0}, {-1, 0}, {0, 0}}) {//要么相邻的走过来,要么自己停留不动 int tx = i + dx, ty = j + dy; if (tx < 0 or tx >= H or ty < 0 or ty >= W or dp[k][tx + 1][ty + 1] == -1) {continue;} dp[k + 1][i + 1][j + 1] = std::max(dp[k + 1][i + 1][j + 1], dp[k][tx + 1][ty + 1] + a[i][j]); } } //枚举在哪里之后无限停留 i64 ans = 0; for (int i = 0; i < H; i++) for (int j = 0; j < W; j++) {ans = std::max(ans, dp[Final_k][i + 1][j + 1] + 1LL * a[i][j] * (K - Final_k));}//在(i, j)停留的最大值 std::cout << ans << '\n'; return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/18253743.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示