紫书 习题8-3 UVa 12545 (构造法)

参考了https://blog.csdn.net/catglory/article/details/47188949


最后推出来操作的个数为问号的个数 加上 同一位置上S串为0而T串为1的位置数量 

与 同一位置上S串为1而T串为0的位置数量的最大值。

也就是max(ans1 + ans2) + que (que为问号总数, 而ans1和ans2意义如上)

这个公式应该是众多博客里面最简单的了, 让我一步一步推给你看


首先判断可不可行, 如果S串1大于T串中1的个数那么就永远不行

因为S串中1的个数只能增加(0变成1或者问号变成1), 不能减少。

所以导致了S串中1的个数永远大于T串中1的个数, 自然不管怎么变永远不能相等


然后考虑怎么变

首先问号变成数字最多可以消去1个差异, 0变成1也是最多消去1个差异, 而交换可以消除两个差异

所以尽量交换, 能交换就交换。

所以第一步就是先尽量交换, 问号先不理它。

交换的操作数怎么求?

交换的时候把0和1交换位置使得相同, 也就是说要“配对”, 也就是一对同一位置上S串是0而T

串是1和同一位置上S串是1而T串是0的两个位置, 这个时候可以交换, 然后位置就匹配上了。

所以我们就要找这样子有几对。

显然, 我们可以求出同一位置上S串是0而T串的位置数ans1, 和同一位置上S串是1而T串为0的位置数

ans2, 那么既然要“配对”, 那么对数就是两者的最小值,多余的后面再处理。

也就是对数 = min(ans1 + ans2)

好了, 交换完了之后,差异有两个, 一个是问号, 一个是交换下来多出的位置。

问号的操作数到最后会加上。

多出的位置要不全是S串为0, T串为1, 要不全是S串为1, T串为0 要不刚好匹配完。


(1)刚好匹配完(ans1 = ans2), 那就把问号全部变成与T串该位置的数字一样就可以了。

那么此时ans1 = ans2, 也就是配对消耗的操作 = min(ans1 + ans2) = ans1(或ans2, 无所谓)

所以总操作数= 配对消耗的操作+问号变化的操作 = ans1 + que(que为问号数)

显然开头那个式子是可以使用这种情况的, 也就是max(ans1 + ans2) + que = ans1 + que


(2)如果匹配完多余的是同一位置上S串为0T串为1(ans1 >ans2)。

那么显然, 把0变成1, 而问号变成T串上该位置的数字

那么额外的操作数就是0变成1, 也就是配对完多余的数目, 也就是ans1 - ans2

所以总操作数 = 配对消耗的操作+问号变化的操作 + 0变成1的操作数

=min(ans1, ans2) + ans1 - ans2 + que

= ans2 + ans1 - ans2 + que

= ans1 + que

这里的ans1是ans1和ans2中的最大值。

所以符合开头讲的式子


(3)如果匹配完多余的是同一位置上S串为1T串为0(ans1 < ans2)。

那么这个时候就只有替换一种可能了(因为只能0变成1不能1变成0)

那么替换哪里的呢, 前面替换好了不动, 那么就替换问号, 问号中

同一位置是1的, 把问号变成0, 然后0和上面说的1替换, 就匹配成功了

所以这个时候的操作数就是多余的个数, 也就是(ans2 - ans1)。

所以总操作数 = ans1 + ans2 - ans1 + que = ans2 +que

ans2依然是ans1和ans2中的最大值, 同理就可以得出开头那个式子。


呼~终于写完了, 真的是大道至简, 推到最后的式子非常的简单。


#include<cstdio>
#include<cstring>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 112;
char s[MAXN], t[MAXN];

int main()
{
	int T, kase = 0;
	scanf("%d", &T);
	
	while(T--)
	{
		int s1 = 0, t1 = 0, ans1 = 0, ans2 = 0, que = 0;
		scanf("%s%s", s, t);
		
		REP(i, 0, strlen(s))
		{
			if(s[i] == '1') s1++;
			if(t[i] == '1') t1++;
			if(s[i] == '?') que++;
			if(s[i] != t[i] && s[i] == '1') ans1++;
			if(s[i] != t[i] && s[i] == '0') ans2++;
		}
		
		if(s1 > t1) printf("Case %d: -1\n", ++kase);
		else printf("Case %d: %d\n", ++kase, que + max(ans1, ans2));
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_34416123/article/details/80178168