ALGORITHM NOTE / ARCHIVE

2024杭电多校06——1005交通管控

大意 一个操作杆可以对k个红绿灯进行操作,操作杆上的一个字符对应一个红绿灯,操作包括+,-,0,问每种组合方案有多少种组合方式 +: red->green->yellow->red -:green->red->yellow->green

补题点这里

文章插图

大意 一个操作杆可以对k个红绿灯进行操作,操作杆上的一个字符对应一个红绿灯,操作包括+,-,0,问每种组合方案有多少种组合方式 +: red->green->yellow->red -:green->red->yellow->green

可以用一个三进制数表示每个灯的状态,因为k最多只有10个, , 仔细看会发现+,-操作一个是顺时针转一圈,一个是逆时针转一圈,用分别表示绿色,黄色,和红色,那么其实就相当于加一再模3,和减一再模三的操作. 每个操作杆用或不用可以考虑类似01背包的动态规划. 设f[i]表示i这个状态有多少种方案 其中是j经过操作杆变化而来的, 所以其实本来的方程有二维,表示的是用到第几个操作杆,但是这一维可以不要,我没有开滚动数组,是另一种常见优化,存储上一轮的结果,用上一轮的结果进行转移,时间复杂度大概是 最多次,开了可以接受

最后输出答案的话,我写了个,sort了一下,不知道有没有更快的方法,有的话告诉我一下

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using i64 = long long;


i64 square3[100];
i64 n,k,mod;

//绿色是0,红色是2,黄色是1
inline void solve(){
    cin>>n>>k>>mod;
    vector<string> op(n+1);
    for(int i = 1;i<=n;++i) cin>>op[i];
    vector<i64> f(square3[k]+1,0),f1;//f[i]:i这个状态有多少种方案
    vector<bool> vis(square3[k]+1,0),vis1;
    f[0]=1LL;
    vis[0]=1;
    for(int i = 1;i<=n;++i){
        f1 = f;
        vis1 = vis;
        //x x x x x x   Base3
        //0 1 2 3 4 k-1 遍历顺序,操作顺序
        //k-1 k-2 ...0  位次
        for(int j = 0;j<square3[k];++j){
            if(vis1[j]==0) continue;
            int tmp=0;
            for(int s = 0;s<k;++s){
                int t = (j/square3[k-1-s])%3;
                if(op[i][s]=='+') (t+=1)%=3;
                else if(op[i][s]=='-') (t+=2)%=3;
                tmp = tmp*3+t;
            }
            f[tmp] = (f[tmp]+f1[j])%mod;
            vis[tmp]=1;
        }
    }
    vector<pair<string,int>>p;
    //map<string,int> mp;应该也能过
    for(int i = 0;i<square3[k];++i){
        if(!vis[i]) continue;
        string ss;
        for(int j = k-1;j>=0;--j){
            int t = (i/square3[j])%3;
            ss+=char('A'+t);
        }
        p.push_back({ss,f[i]%mod});
        //mp[ss] = f[i]%mod;
    }
    sort(p.begin(),p.end());
    for(auto i:p){
        cout<<i.first<<" "<<i.second%mod<<"\n";
    }
}

signed main(){
    ios;
    square3[0]=1LL;
    for(int i =1;i<=15;++i) square3[i] = square3[i-1]*3LL;
    int t;cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

写代码途中遇到的问题,TLE,vector直接开60000就会T,要根据k来开,想要更快的话其实可以再读入的时候就开始操作(看std学的).取某一位三进制,进位退位这种操作有比较简单的写法,(写一堆内联函数就显得有点愚蠢->me)