【OI思维题】黑白棋:如何从千百的乱象中寻找自己的答案?

1 篇文章 0 订阅
1 篇文章 0 订阅

黑白棋

题目描述

小 Z 迷上了棋类游戏,聪明的他很快就摸清了黑白棋、五子棋和围棋的规则,但今天 Prof. Wang 给了他一项挑战:需将杂乱的棋子按黑白相间的特定序列排列。小 Z 决定通过交换棋子位置的方式将它们排列整齐,对于位置分别为 i , j i,j i,j 的两颗棋子,需要花费 ∣ i − j ∣ k \lvert i-j\rvert^k ijk 的代价完成交换。杂乱无章的棋子让小Z头疼不已,你能帮帮他,找到让代价最小的交换方式吗?

输入格式

第一行包含一个整数 T T T 表示测试数据的种类数.

接下来 2 ⋅ y 2 \cdot y 2y 行,对于每组测试数据,第一行为一个整数 k k k ,表示代价函数的类型,第二行一个由 BW 组成的长度为 l l l 的字符串,分别表示黑色和白色。

输出格式

T T T 行, 每行输出一个整数,表示最小的总代价。如果不存在一种方案使得棋子黑白相间排列,输出 − 1 -1 1

样例输入

1
2
BWBWW

样例输出

2

样例解释

交换 1 1 1 位置的黑点和 2 2 2 位置的白棋,以及 3 3 3 位置的黑棋和 4 4 4 位置的白棋,序列变为 WBWBW,代价为 2。可以证明这是代价最小的一种方案。

数据范围

每组测试数据保证 1 ≤ T ≤ 5 1 \le T \le 5 1T5

对于 20 % 20 \% 20% 的数据, k = 0 , 1 ≤ l ≤ 10 k=0,1 \le l \le 10 k=0,1l10

对于 40 % 40 \% 40% 的数据, k = 0 , 1 ≤ l ≤ 100 k=0,1 \le l \le 100 k=0,1l100

对于 70 % 70 \% 70% 的数据, k = 0 , 1 ≤ l ≤ 1000 k=0,1 \le l \le 1000 k=0,1l1000

对于 100 % 100 \% 100% 的数据, k = 0 , 1 ≤ l ≤ 1 0 5 k=0,1 \le l \le 10^5 k=0,1l105

黑白棋 题解

20 % 20\% 20% 的数据可以爆搜,这里不详细说了,我们重点讲正解。

几个非常显而易见的性质:我们总不能动位置已经正确的棋子;当黑色、白色棋子数量差大于 1 的时候,一定无解。

其实做着做着可以发现这个 k k k 是没有用的,因为对于 i i i j j j 的对调,显然最优的对调方案是相邻的两个黑白棋对调,一眼丁真这肯定是最优的。相邻两个棋子,意味着它们的位置差是 1,即 1 k 1^k 1k 永远等于 1。

那么扩展开来,对于两个不相邻的错误棋子,它们在错误棋子的序列中是连续的,尽管实际位置不同,但总可以通过上述方法调换。

举个例子:若错误的黑色棋子位置是 i i i,离它最近的下一个错误的白色棋子位置是 j j j,那么将它俩移动到正确位置的代价是 ∣ i − j ∣ \lvert i - j \rvert ij

好的,现在经过一通分析,做法也呼之欲出:从前往后遍历整个棋子序列,如果发现有错误的棋子,就查看我们的“错误棋子队列”:如果队列内有在它前面的、异色的、错误的棋子,就把该棋子取出队列和它进行调换;如果没有,就将该棋子放入队列。

最后,需要注意一点:如果k是0,那么无论两棋子间距离有多大,调换的代价都是 1。

代码:

#include <bits/stdc++.h>
using namespace std;
int t , k;
string s;
int sum0 , sum1;
queue <int> q;
int main() {
//	freopen("1.in" , "r" , stdin);
//	freopen("1.out" , "w" , stdout);
	cin >> t;
	while(t --) {
		while(!q.empty()) q.pop();
		sum0 = sum1 = 0;
		int ans = 0;
		cin >> k;
		cin >> s;
		int lens = s.length();
		s = " " + s;
		for(int i = 1 ; i <= lens ; ++ i) {
			if(s[i] == 'W') sum1 ++;
			else sum0 ++;
		}
		if(abs(sum1 - sum0) > 1) {
			cout << "-1" << endl;
			continue;
		}
		if(sum0 > sum1) {
			for(int i = 1 ; i <= lens ; ++ i) {
				if(i % 2 == 0 && s[i] == 'B') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'W') {
							q.pop();
							if(k == 0) ans ++;
							else ans += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
				else if(i % 2 == 1 && s[i] == 'W') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'B') {
							q.pop();
							if(k == 0) ans ++;
							else ans += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
			}
		}
		else if(sum0 < sum1) {
			for(int i = 1 ; i <= lens ; ++ i) {
				if(i % 2 == 0 && s[i] == 'W') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'B') {
							q.pop();
							if(k == 0) ans ++;
							else ans += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
				else if(i % 2 == 1 && s[i] == 'B') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'W') {
							q.pop();
							if(k == 0) ans ++;
							else ans += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
			}
		}
		else {
			int cnt = 0;
			for(int i = 1 ; i <= lens ; ++ i) {
				if(i % 2 == 0 && s[i] == 'W') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'B') {
							q.pop();
							if(k == 0) cnt ++;
							else cnt += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
				else if(i % 2 == 1 && s[i] == 'B') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'W') {
							q.pop();
							if(k == 0) cnt ++;
							else cnt += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
			}
			for(int i = 1 ; i <= lens ; ++ i) {
				if(i % 2 == 0 && s[i] == 'B') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'W') {
							q.pop();
							if(k == 0) ans ++;
							else ans += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
				else if(i % 2 == 1 && s[i] == 'W') {
					if(!q.empty()) {
						int t = q.front();
						if(s[t] == 'B') {
							q.pop();
							if(k == 0) ans ++;
							else ans += abs(i - t);
						}
						else q.push(i);
					}
					else q.push(i);
				}
			}
			ans = min(ans , cnt);
		}
		cout << ans << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值