航电多校第六场 1005
.交通管控
- 题意有
个操作,每个操作为一个字符串,代表从 个红绿灯的变化,初始 个灯全为绿灯,变化顺序为 ,+
代表每个灯变为下一种状态 ,-
代表每个灯变为上一种状态,0
代表没有变化, 问你这n个操作能组成的红绿灯的状态的数量
其实就是那些操作选和不选,暴力的话可以二进制枚举,但是只能接受
以内的大小,这里因为赛时的 原因,还是写了个暴力对拍。
放个暴力对拍的代码
void solve(){
ll n, k, M;
cin >> n >> k >> M;
vector<string> s(n + 1);
for(int i = 0; i < n; i ++) cin >> s[i];
map<string, int> mp;
for(int i = 0; i < (1 << n); i ++){
vector<ll> now(k, 0);
for(int j = 0; j < n; j ++){
if(i >> j & 1){
for(int p = 0; p < k; p ++){
if(s[j][p] == '+') now[p] ++;
else if(s[j][p] == '-') now[p] --;
}
}
}
for(int p = 0; p < k; p ++){
now[p] %= 3;
now[p] += 3;
now[p] %= 3;
}
string s1;
for(int j = 0; j < k; j ++){
ll x = now[j];
if(x == 0) s1 += 'A';
else if(x == 1) s1 += 'B';
else s1 += 'C';
}
mp[s1] ++;
mp[s1] %=M;
}
for(auto [x, y] : mp){
cout << x << ' ' << y % M << endl;
}
}
signed main(){
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t = 1;
// cin >> t;
while(t --){
solve();
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
对于这种求方案数的问题,可以考虑
,这里因为牵扯到 个灯的颜色变化,可以开 维数组,但是程序没法自动开,所以只剩状态压缩这一种办法, 个灯代表 个位置,每个位置有三种状态,即有 种状态,k最大为10,一种有 看作 , 时间复杂度为 大约 左右,限制给了 秒,复杂度很正确了,接下来考虑状态定义 dp[i][j]
代表用前个操作达到 状态的方案数,答案就是 dp[i][state]
,代表出现过的状态,状态转移, ,这里赛时用了一维的st数组标记的,导致样例对,交上去时wa,因为这里的 st[j]
时当前这一层的状态,但是我们需要的是 时的标记状态,导致得到错误的答案,赛时我们的判断有没有出现过的依据是 这里其实是有问题的涉及到取模,可能会取模成 ,导致漏了某些合法方案数,所以 st
数组要开成维,没有一个合法方案就 代表这种方案出现过,注意更新的时候要算上不要这种操作的情况,也就是 转移,这里也要更新 st
数组。初始的状态是,代表初始状态合法,这里空间有点极限,可以考虑滚动数组优化
void solve(){
int n, k, m; cin >> n >> k >> m;
vector<string> s(n + 1);
for(int i = 1; i <= n; i ++) {
cin >> s[i];
}
int sum = pow(3, k);
vector<vector<int>> dp(2, vector<int> (sum + 100));
auto st = dp;
dp[0][0] = 1;
st[0][0] = 1;
auto n_to_s = [&](int x) {
string res;
while(x) {
res += (x % 3) + 'A';
x /= 3;
}
while(res.size() < k) res.push_back('A');
reverse(res.begin(), res.end());
return res;
};
auto s_to_n = [&](string x) {
int res = 0;
for(int i = 0; i < x.size(); i ++) {
res = res * 3 + (x[i] - 'A');
}
return res;
};
for(int i = 1; i <= n; i ++) {
for(int j = 0; j < sum; j ++) dp[i & 1][j] = 0;
for(int j = 0; j < sum; j ++) {
dp[i & 1][j] += dp[(i - 1) & 1][j];
if(st[(i - 1) & 1][j]) {
st[i & 1][j] = 1;
}
dp[i & 1][j] %= m;
string t = n_to_s(j);
for(int p = 0; p < k; p ++) {//更新灯的状态
if(s[i][p] == '+') {
if(t[p] == 'C') t[p] = 'A';
else t[p] ++;
} else if(s[i][p] == '-') {
if(t[p] == 'A') t[p] = 'C';
else t[p] --;
}
}
//cout << t << endl;
int state = s_to_n(t);
dp[i & 1][state] += dp[(i - 1) & 1][j];
if(st[(i - 1) & 1][j]) {
//cout << n_to_s(j) << ' ' << t << endl;
st[i & 1][state] = 1;
}
dp[i & 1][state] %= m;
}
}
for(int i = 0; i < sum; i ++) {//从小到大就是字典序了
if(st[n & 1][i]) {
cout << n_to_s(i) << ' ' << dp[n & 1][i] << endl;
}
}
}
int main()
{
IOS;
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
本文作者:zouyua
本文链接:https://www.cnblogs.com/ZouYua/p/18344288
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步