Double or Noting - Google Codejam 2021 Round 1C

博客介绍了Google Codejam 2021 Round 1C的第三题,讨论了如何通过双倍或取反操作将一个二进制数转换成另一个二进制数的问题。内容包括题目的详细描述、解题思路以及模拟变换的策略,重点在于找到字符串的公共后缀并进行操作模拟。
摘要由CSDN通过智能技术生成

Double or Noting - Google Codejam 2021 Round 1C第三题

You are given a starting non-negative integer S and an ending non-negative integer E. Both S and E are given by their binary representation (that is, they are given written in base 2). Your goal is to transform S into E. The following two operations are available to you:
Double your current value.
Take the bitwise NOT of your current value. The binary representation of your current value is taken without unnecessary leading zeroes, and any unnecessary leading zeroes produced by the operation are dropped. (The only necessary leading zero is the one in the representation of 0).
For example, by using the double operation, 6 becomes 12, 0 becomes 0, and 10 becomes 20. By using the NOT operation, 0 becomes 1, 1 becomes 0, 3=112 becomes 0, 14=11102 becomes 1, 10=10102 becomes 5=1012, and 5=1012 becomes 2=102. (X2 means the integer whose binary representation is X).
You can use these operations as many times as you want in any order. For example, you can transform S=100012 to E=1112 using the NOT operation first, then using the double operation twice, and then another NOT operation:
100012⟹NOT11102⟹×2111002⟹×21110002⟹NOT1112.
Determine the smallest number of operations needed to complete the transformation, or say it is impossible to do so.

题目大意:

你有两个01字符串S和E,你的任务是对S进行总次数尽量少的非(~)或者左移一位(<<1)的操作把它变成E。输出最少操作次数;若无法完成,输出"IMPOSSIBLE"。

思路分析:

今年Round 1三轮之中最简短也是最像算法题的压轴题了。对于这种题,相信对字符串手到擒来的同学们早已经轻车熟路了。由于该题对时间复杂度基本没什么要求(两个串都不长,最多只有100个字符),我们考虑直接枚举S和E的公共后缀(前缀),对于每一种长度的后缀直接模拟把S变成E的过程,最后取最小值即可。

模拟的方法也比较容易:连续的0或者1直接继续左移,否则非操作。如果这样最后得到的不是E,那说明保留当前后缀的状况下不可能。具体看代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int T;
string inv(string a)//由于是字符串,建议自己写一个非运算
{
    string res = "";
    for (char c : a) {
        if (c == '0') res += '1';
        else if ((int)res.length() > 0) res += '0';//注意不要前缀0
    }
    if((int)res.length() == 0) return "0";//特判一下
    return res;
}
int work(string s, string e, int len)//len代表当前建好e的部分的长度
{
    int res = 0;
    while (res <= 200 && s != e)//多加个停止条件总不会错
	{
        if (len < (int)e.length() && s[0] == '1')//特判掉s是0的情况,因为怎么左移都没用
		{  
			if(s.back() == '0')
			{
				if(len?e[len-1]:'0' == e[len])//如果和前一位相同,注意特判是第一位的情况
				{
					s += '0';
            		res++, len++;
					continue;
				}
			}
			else if(len?e[len-1]:'0' != e[len])//最后一位是1,加0相当于把两个连续01串隔开
			{
					s += '0';
            		res++, len++;
					continue;
			}
        }
        s = inv(s);//否则进行否操作
        res++;
    }
    if (s == e) return res;
    else return inf;
}
int main() {
    scanf("%d",&T);
    for (int ca = 1; ca <= T; ca++) {
        string s,e;
        cin>>s>>e;
        printf("Case #%d: ", ca);
		int res = inf;
    	for (int i = max(0,(int)s.length()-(int)e.length()); i <= (int)s.length(); i++)//枚举后缀长度
		{
        	int len = (int)s.length()-i;
        	string ss = s.substr(i, len);
        	string ee = e.substr(0, len);
        	if(ss == ee||inv(ss) == ee)res = min(res, work(s, e, len));//找到可能的公共前(后)缀
    	}
        if (res == inf) puts("IMPOSSIBLE");
        else printf("%d\n",res);
    }
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值