2.27 CW 模拟赛 T1. 麻将

news/2025/2/27 20:13:37/文章来源:https://www.cnblogs.com/YzaCsp/p/18741839

前言

  • 定义合法情况, 要求输出一组合法情况 / 合法情况的最值问题 / 求方案数
    • 往往利用 \(\rm{dp}\) , 结合约束处理当前方案数
      • 关注构造方案 / 顺序
      • 关注本质重复的转移是否存在
    • 先找到一组合法解, 然后在基础上进行调整
    • 找到所有情况统一的构造方案

思路

题意

给定 nn 个位置, 第 ii 个位置是牌 aia_i
求有多少种把牌划分成面子的方法

即形如 {x,x,x} or {x,x+1,x+2}\{x, x, x\} \textrm{ or } \{x, x + 1, x + 2\} 的连续段

首先因为原串上不好处理, 而且明确给了值域, 所以不难想到转移到值域上去思考

考虑一个合法解的构造过程
在值域上从前往后扫, 每次挑选符合题意的一个连续段然后对值域数组进行更新

一些小问题

首先, 不能简单地对值 ii 分类讨论成

  • (1)(1) 作为 aa{i,i,i}\{i, i, i\}
  • (2)(2) 作为 bb{i,i+1,i+2}\{i, i + 1, i + 2\}
    以此清空 i,i+1,i+2i, i + 1, i + 2 对应的数量

原因是对于一些情况
按照上面的方法, 不会考虑到先对 i,i+1,i+2i, i + 1, i + 2 做操作, 然后再对 i+1,i+2,i+3i + 1, i + 2, i + 3 做操作使 i+1,i+2i + 1, i + 2 清空

也就是说, 这样转移被严格限制了, 不可能生成其他情况的 (2)(2) 操作
因此是不行的


赛时为了解决上面的问题, 换成了对值域的区间 dp\rm{dp}
但是这个问题更加明显
假设对于区间 {a,b,c}\{a, b, c\} , 容易发现如果用区间 dp\rm{dp} , 会分成

  • 清空 {b,c}\{b, c\} , 在清空 aa
  • 清空 {a,b}\{a, b\} , 在清空 cc

不难发现在本质上极容易重复, 并且不易去重

综上, 如何找到一个好的方法来 \(\rm{dp}\)
要满足两个需求

  • 顺序必须严格钦定
  • 必须考虑到所有可能的操作区间

因此不难想到对最初的 \(\rm{dp}\) 做一些修改
并不钦定一定要在当前对 \(i\) 的操作清空 \(i, i + 1, i + 2\) , 而是只钦定清空 \(i - 2\) , 因为再不清空就没机会了

不难想到状态定义
\(f_{i, j, k}\) 表示值域上考虑到 \(i\) , \(i - 1\) 还剩下 \(j\) 个, \(i - 2\) 还剩下 \(k\) 个的方案数

因为每次转移只考虑 \(i, i - 1, i - 2\)
不难发现我们可以钦定每次操作只对 \(i\) 进行 \(3\) 连操作, 然后对 \(\{i, i - 1, i - 2\}\) 进行操作
这样可以避免重复

所以枚举对 \(i\) 进行 \(q\)\(3\) 连操作 , 现在 \(i\) 出现 \(w_i - 3q\)
然后剩下必须进行 \(i - 2\) 次操作, 转移即可

实现

框架

如上转移即可

代码
#include <bits/stdc++.h>
const int MOD = 1e9 + 7;
const int MAXN = 5206; // 41
namespace calc {int add(int a, int b) { return a + b >= MOD ? a + b - MOD : a + b; }int mus(int a, int b) { return a - b < 0 ? a - b + MOD : a - b; }int mul(int a, int b) { return (a * b * 1ll) % MOD; }void addon(int &a, int b) { a = add(a, b); }void mulon(int &a, int b) { a = mul(a, b); }
} using namespace calc;int n, m;
int p[MAXN];
int dp[2][MAXN][MAXN];int now = 0, nxt = 1;/*初始化*/
void init() {if (m == 1 || m == 2) {int ans = 1; for (int i = 1; i <= m; i++) if (p[i] % 3) ans = 0;printf("%d", ans);exit(0);}for (int i = 0; i <= p[1]; i += 3) for (int j = 0; j <= p[2]; j += 3) dp[now][p[2] - j][p[1] - i] = 1;
}signed main()
{scanf("%d %d", &n, &m);for (int i = 1, tmp; i <= n; i++) scanf("%d", &tmp), p[tmp]++;init();for (int i = 3; i <= m; i++) {/*初始化*/ for (int j = 0; j <= p[i]; j++) for (int k = 0; k <= p[i - 1]; k++) dp[nxt][j][k] = 0;for (int j = 0; j <= p[i - 1]; j++) for (int k = 0; k <= std::min(p[i - 2], j); k++) {for (int q = 0; q <= p[i] && p[i] - q >= k; q += 3) {addon(dp[nxt][p[i] - q - k][j - k], dp[now][j][k]);}}std::swap(now, nxt);}printf("%d", dp[now][0][0]);return 0;
} 

总结

一类只钦定消除 不消除以后就不能消除的元素\(\rm{dp}\)
一般记录到达 不消除以后就不能消除的元素 之前的元素还剩下多少个来处理

往往一种操作只用一次转移考虑才能做到去重


关于这道题的一些额外理解

首先, 一组合法情况可以视作对值域数组 ww 的一个构造

  • 对于 wiw_i , 进行 pp 次操作构造 pp{i,i,i}\{i, i, i\}
  • 对于任意 wi,wi1,wi2w_i, w_{i - 1}, w_{i - 2} , 进行 qq 次操作

如果恰好把 ww 归零, 即是合法构造

因此我们枚举 ii 作为分界, 同时对这两个进行构造

类似于之前多重集排列那一部分, 这个问题同样可以表述为
求有多少组组 x,yx, y , 使其满足
i,wi3xi(yi+yi+1+yi+2)=0\forall i, w_i - 3x_i - (y_i + y_{i + 1} + y_{i + 2}) = 0
然后稍微转化一下, 把 yiy_i 的贡献拆成 i,i1,i2i, i - 1, i - 2 处的贡献, 就可以做 dp\rm{dp}

这也是一种理解 dp\rm{dp} 的方法, 即先把构造表示出来, 再面向构造做
一般适用于这种不常规的 dp\rm{dp} 问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/890803.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

题解:P4586 [FJOI2015] 最小覆盖双圆问题

写了这么久终于过了,发篇题解记录一下。 第一次写黑题题解,写的不好请见谅。 目录本题思路 三点定圆 最小圆覆盖 关于最小圆覆盖时间复杂度 回到本题 二分法划分点集 总时间复杂度 最小覆盖双圆问题代码本题思路 首先,这道题叫做最小覆盖双圆问题,这道题涉及到一个叫做最小…

sed undefined label on MacOS, FreeBSD

A quick fix is to prepend your string expression with an empty string: For example: instead of sed -i s/foo/bar/g text.txt write: sed -i s/foo/bar/g text.txtThis should work across different OS (Linux, MacOS, Windows) --dopexxx来源:http://stackoverflow.…

软工作业1:自我介绍+软工5问

这个作业属于哪个课程 软工23级这个作业要求在哪里 自我介绍+软工五问这个作业的目标 熟悉博客园以及Github的相关操作,了解软件工程相关内容1.自我介绍兴趣爱好:羽乒人,爱听歌,电影,楷书,行楷 编程语言:C,JAVA 目标:后端开发2.软工五问软件工程在对就业上的帮助的具体…

对“推箱子”小游戏代码的改进

一.代码来源 https://www.cnblogs.com/heyu123/p/14844284.html 二.运行环境 DEV--C++ 三.原代码及其运行结果 原代码: int map[8][8]={{1,1,1,1,1,1,1,1},//0 空地 {1,0,0,0,1,0,0,1},//1 墙 {1,0,1,0,1,4,3,1},//3 目的地 {1,0,0,0,0,4,3,1},//4 箱子 {1,0,1,0,1,4,3,1},//5…

软件开发与创新课程设计作业——软件逆向设计

一、来源:软件工程2班李鹏飞去年的大作业`点击查看代码 #include <iostream> #include <string> #include <fstream> using namespace std;//定义客户类型 enum eGuestType // 在高版本VS中,需要用enum class,在低版本的vs中,直接用enum也可以 {e_member…

LVI_SAM 虚拟机安装复现(一)

0. 前言 高能警告:LVI_SAM 的安装步骤是繁琐的,一个坑接着一个坑,请预留48+小时的安装时间,和80%以上的san值。非战斗人员请尽快撤离。 预备知识:虚拟机安装步骤,ROS基本概念,Makefile工作原理 没有预备知识的话,也没关系,本文也不会给你解释的( 本文是第一大步骤,即…

大模型--三种三种检索方式-Dense retrieval / Lexical Retrieval / Multi-Vector Retrieval- 44

1. 参考 M3-Embedding https://github.com/FlagOpen/FlagEmbedding https://arxiv.org/pdf/2402.03216 https://huggingface.co/BAAI/bge-m3 2. Dense retrievalimport torch import torch.nn as nnclass DenseRetrieval(nn.Module):def __init__(self, embedding_dim):super(D…

从拉新到留存,用户生命周期分析全流程

已收藏分享从拉新到留存,用户生命周期分析全流程 2025-02-17 17:02人人都是产品经理在当今竞争激烈的市场环境中,理解并管理用户生命周期是实现用户增长和留存的关键。本文将深入剖析用户生命周期的全流程管理,从拉新到留存,详细解读不同业务类型(如消费品、耐用品、平台型…

朴素贝叶斯其实并不朴素

朴素贝叶斯英文名称NaiveBayes,朴素贝叶斯确实nave,但是并不朴素,而是简单,并不是逻辑上面的简单,而是假设上面的简单。 1.贝叶斯公式 ​ 其中: P(C|X)是类C在给定特征X下的后验概率。 P(X|C)是特征X在给定类C下的条件概率,也叫做似然。 P(C)是类C的先验概率。 P(X)是特…

NocoBase 本周更新汇总:新增路由管理

本周更新包括:支持为页面标签页配置权限,新增路由管理页面等。汇总一周产品更新日志,最新发布可以前往我们的博客查看。 NocoBase 目前更新包括的版本更新包括三个分支:main ,next和 develop。main :截止目前最稳定的版本,推荐安装此版本。 next:包含即将发布的新功能,…

AI安全-模型用户输入注入

顾名思义,在调用AI大模型时,根据用户传入的数据,进行AI处理,调用插件,但模型后端需要调用API,API需要传入的username一个小场景,企业微信对话调用AI去修改当前密码 假设开发者设计如下: 用户输入-> AI -> 调用插件修改密码 修改密码插件实现:a.com/change_passw…