题解 S

传送门

  • 有多种颜色的序列可以交换相邻项,求最小方案数:注意同种颜色内顺序是否恒不变
  • 序列可以交换相邻项,求最小方案数:注意如果给每个点按输入顺序编号,那最小方案数就是结果序列中逆序对的最小数量

于是问题就被转化为了求最小逆序对数量
考虑DP,令 \(dp[i][j][k][0/1/2]\) 表示三种颜色分别选到第几个,最后一个球的颜色是哪一种的最小逆序对数
刷表转移,通过预处理将每种颜色的第 \(i\) 个数映射到原轴上求新增加的逆序对数

  • 特别注意__int128不能用unordered_map!会直接CE掉,编译时建议直接加上 -std=c++14
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll __int128
//#define int long long

int n;
char s[N];

namespace force{
	int a[N], buc[5];
	queue<ll> q;
	map<ll, int> dp;
	inline ll gets() {
		ll ans=0;
		for (int i=1; i<=n; ++i) ans=ans*10+a[i];
		return ans;
	}
	void solve() {
		ll tem=0;
		for (int i=1; i<=n; ++i) {
			if (s[i]=='R') s[i]=0;
			else if (s[i]=='G') s[i]=1;
			else s[i]=2;
			tem=tem*10+s[i];
			++buc[s[i]];
		}
		sort(buc, buc+3);
		if (buc[0]+buc[1]+1<buc[2] || n>20) {puts("-1"); exit(0);}
		// int cnt=0;
		dp[tem]=0;
		q.push(tem);
		while (q.size()) {
			// ++cnt;
			ll s=q.front(); q.pop();
			int dp_this=dp[s];
			for (int i=n; i; --i) a[i]=s%10, s/=10;
			// cout<<"s: "; for (int i=1; i<=n; ++i) cout<<a[i]<<' '; cout<<endl;
			// cout<<"dp: "<<dp_this<<endl;
			for (int i=1; i<n; ++i) if (a[i]==a[i+1]) goto jump;
			printf("%d\n", dp_this);
			// cout<<"cnt: "<<cnt<<endl;
			exit(0);
			jump: ;
			for (int i=1; i<n; ++i) {
				if (a[i]!=a[i+1]) {
					swap(a[i], a[i+1]);
					ll tem=gets();
					if (dp.find(tem)==dp.end()) dp[tem]=dp_this+1, q.push(tem);
					swap(a[i], a[i+1]);
				}
			}
		}
	}
}

namespace task{
	int buc[5], cnt0, cnt1, cnt2;
	int sum0[N], sum1[N], sum2[N], pos0[N], pos1[N], pos2[N];
	int dp[210][210][210][3];
	inline int rev0(int i, int j) {return max(sum0[j]-sum0[i], 0);}
	inline int rev1(int i, int j) {return max(sum1[j]-sum1[i], 0);}
	inline int rev2(int i, int j) {return max(sum2[j]-sum2[i], 0);}
	void solve() {
		for (int i=1; i<=n; ++i) {
			if (s[i]=='R') s[i]=0, pos0[++cnt0]=i, ++sum0[i];
			else if (s[i]=='G') s[i]=1, pos1[++cnt1]=i, ++sum1[i];
			else s[i]=2, pos2[++cnt2]=i, ++sum2[i];
			++buc[s[i]];
		}
		if (buc[0]+buc[1]+1<buc[2]) {puts("-1"); exit(0);}
		for (int i=1; i<=n; ++i) sum0[i]+=sum0[i-1];
		for (int i=1; i<=n; ++i) sum1[i]+=sum1[i-1];
		for (int i=1; i<=n; ++i) sum2[i]+=sum2[i-1];
		memset(dp, 0x3f, sizeof(dp));
		dp[1][0][0][0]=dp[0][1][0][1]=dp[0][0][1][2]=0;
		for (int i=0; i<=cnt0; ++i) {
			for (int j=0; j<=cnt1; ++j) {
				for (int k=0; k<=cnt2; ++k) {
					for (int s=0; s<3; ++s) if (dp[i][j][k][s]<INF) {
						// cout<<i<<' '<<j<<' '<<k<<' '<<s<<endl;
						if (s!=0 && i<cnt0) dp[i+1][j][k][0]=min(dp[i+1][j][k][0], dp[i][j][k][s]+rev1(pos0[i+1], pos1[j])+rev2(pos0[i+1], pos2[k]));
						if (s!=1 && j<cnt1) dp[i][j+1][k][1]=min(dp[i][j+1][k][1], dp[i][j][k][s]+rev0(pos1[j+1], pos0[i])+rev2(pos1[j+1], pos2[k]));
						if (s!=2 && k<cnt2) dp[i][j][k+1][2]=min(dp[i][j][k+1][2], dp[i][j][k][s]+rev0(pos2[k+1], pos0[i])+rev1(pos2[k+1], pos1[j]));
					}
				}
			}
		}
		printf("%d\n", min(min(dp[cnt0][cnt1][cnt2][0], dp[cnt0][cnt1][cnt2][1]), dp[cnt0][cnt1][cnt2][2]));
		exit(0);
	}
}

signed main()
{
	freopen("s.in", "r", stdin);
	freopen("s.out", "w", stdout);

	scanf("%d%s", &n, s+1);
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2021-10-17 09:22  Administrator-09  阅读(1)  评论(0编辑  收藏  举报