1
希望对你有帮助.|

zouyua

园龄:1年10个月粉丝:3关注:3

航电多校第六场 1005

.交通管控

  • 题意有n个操作,每个操作为一个字符串,代表从k个红绿灯的变化,初始k个灯全为绿灯,变化顺序为绿, + 代表每个灯变为下一种状态 , -代表每个灯变为上一种状态, 0代表没有变化, 问你这n个操作能组成的红绿灯的状态的数量
  • 1<=n<=500,1<=k<=10,2<=m<=1e9+7

其实就是那些操作选和不选,暴力的话可以二进制枚举,但是只能接受 n<=20 以内的大小,这里因为赛时的debug原因,还是写了个暴力对拍。
放个暴力对拍的代码

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;
}

对于这种求方案数的问题,可以考虑dp,这里因为牵扯到k个灯的颜色变化,可以开k维数组,但是程序没法自动开,所以只剩状态压缩这一种办法,k个灯代表k个位置,每个位置有三种状态,即有3k种状态,k最大为10,一种有3k=59049看作1e5, 时间复杂度为O(n3kk) 大约5e8左右,限制给了10秒,复杂度很正确了,接下来考虑状态定义 dp[i][j] 代表用前i个操作达到j状态的方案数,答案就是 dp[i][state]state代表出现过的状态,状态转移,dp[i][state]=dp[i1][j]j,这里赛时用了一维的st数组标记的,导致样例对,交上去时wa,因为这里的st[j]时当前这一层的j状态,但是我们需要的是i1时的标记状态,导致得到错误的答案,赛时我们的判断有没有出现过的依据是dp[i1][j]>0 这里其实是有问题的涉及到取模,可能会取模成0,导致漏了某些合法方案数,所以st数组要开成n维,没有一个合法方案就st[i][state]=1 代表这种方案出现过,注意更新的时候要算上不要这种操作的情况,也就是dp[i][j]=dp[i1][j]转移,这里也要更新st数组。初始的状态是dp[0][0]=1,st[0][0]=1,代表初始状态合法,这里空间有点极限,可以考虑滚动数组优化

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 中国大陆许可协议进行许可。

posted @   zouyua  阅读(12)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起