在牛客网上打的,我还蛮喜欢这些大学的比赛的,比打VJ,CF和CCPC,ICPC的往年题目有趣多了,可能是外国人的脑洞我不习惯吧,中国人出的题还是舒服。
这里记录一下B,因为做这个题的过程对我很有启发。
一开始是这样的代码:
#include <bits/stdc++.h>
using namespace std;
const int manx=1e5+10;
int t;
int s[manx],s2[manx];
char a[manx],b[manx],c[manx];
int main()
{
scanf("%d",&t);
while(t--)
{
memset(s,0,sizeof(s));
memset(s2,0,sizeof(s2));
scanf("%s",a);
scanf("%s",b);
int n=strlen(a);
int k=1,k2=1;
for(int i=0; i<n; i++)//两重循环,加三个if判断,这一代码块会超时
{
if(a[i]!=b[i])
{
for(int j=i; j<n; j++)
{
if(a[j]=='0')
{
a[j]='1';
}
else if(a[j]=='1')
{
a[j]='0';
}
}
s[k++]=i;
}
}
s2[k2++]=-1;
for(int i=0;i<n;i++) //还用遍历方式更新值,也很浪费时间
{
c[i]='0';
}
for(int i=0; i<n; i++) //同第一个大循环,两重循环三个判断,超时
{
if(c[i]!=b[i])
{
for(int j=i; j<n; j++)
{
if(c[j]=='0')
{
c[j]='1';
}
else if(c[j]=='1')
{
c[j]='0';
}
}
s2[k2++]=i;
}
}
//printf("%s %s\n",a,c);
//printf("%d %d\n",k,k2);
if(k<=k2)
{
for(int i=1; i<k; i++)
{
if(i!=k-1)
printf("%d ",s[i]+1);
else
printf("%d\n",s[i]+1);
}
}
else
{
for(int i=1; i<k2; i++)
{
if(i!=k2-1)
printf("%d ",s2[i]+1);
else
printf("%d\n",s2[i]+1);
}
}
}
return 0;
}
这是最开始,我想着模拟(虽然知道很可能超时,但是还是抱着侥幸心理)一种是他不先把数全变成0,直接变;一种是全变成0后再开始变,这样就是两种情况,取步数少的那种。但是我们看到n最大为1e5,我每变一次,就更新这个数后面的所有数的值(1变0,0变1)其时间复杂度立刻增大了一倍,会超时的。
之后我就想到了一句话,山不向我走来,我就向山走去。
我尝试不用每变一个数,后面的数跟着变,而是设一个num,这个num就是我当前数组应该是1或者0,但我用num保存这个值,这样我就不用遍历后面的数了,因为说到底,每次判断变或者不变后面的数(和模板串一样就不变,不一样就变)是由当前这个数决定的,所以我只要用num保存当前数的状态就行啦!
一开始用的不彻底,出现了半成品,依旧超时,只改了下半部分,没改上半部分,因为上半部分不好改。
#include <bits/stdc++.h>
using namespace std;
const int manx=1e5+10;
int t;
int s[manx],s2[manx];
char a[manx],b[manx];
int main()
{
scanf("%d",&t);
while(t--)
{
memset(s,0,sizeof(s));
memset(s2,0,sizeof(s2));
scanf("%s",a);
scanf("%s",b);
int n=strlen(a);
int k=1,k2=1;
for(int i=0; i<n; i++)
{
if(a[i]!=b[i])
{
for(int j=i; j<n; j++)
{
if(a[j]=='0')
{
a[j]='1';
}
else if(a[j]=='1')
{
a[j]='0';
}
}
s[k++]=i;
}
}
s2[k2++]=-1;
char num='0';
for(int i=0; i<n; i++)
{
if(num!=b[i])
{
if(num=='0')
{
num='1';
}
else if(num=='1')
{
num='0';
}
s2[k2++]=i;
}
}
//printf("%d %d\n",k,k2);
if(k<=k2)
{
for(int i=1; i<k; i++)
{
if(i!=k-1)
printf("%d ",s[i]+1);
else
printf("%d\n",s[i]+1);
}
}
else
{
for(int i=1; i<k2; i++)
{
if(i!=k2-1)
printf("%d ",s2[i]+1);
else
printf("%d\n",s2[i]+1);
}
}
}
return 0;
}
之后我又改了上半部分,过了!但是我用的不是num存储当前状态的数字了(0/1)而是用个flag判断当前我是和他相同对,还是和他不同才对。这点可能不好理解,因为确实是当时急中生智想出来的,我也解释不太好,请看代码。
#include <bits/stdc++.h>
using namespace std;
const int manx=1e5+10;
int t;
int s[manx],s2[manx];
char a[manx],b[manx];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%s",a);
scanf("%s",b);
int n=strlen(a);
int k=1,k2=1;
int flag=0;
for(int i=0; i<n; i++)
{
if(a[i]!=b[i]&&flag==0)
{
s[k++]=i;
flag=1;
}
else if(a[i]==b[i]&&flag==1)
{
s[k++]=i;
flag=0;
}
}
s2[k2++]=-1;
char num='0';
for(int i=0; i<n; i++)
{
if(num!=b[i])
{
if(num=='0')
{
num='1';
}
else if(num=='1')
{
num='0';
}
s2[k2++]=i;
}
}
//printf("%d %d\n",k,k2);
if(k<=k2)
{
for(int i=1; i<k; i++)
{
if(i!=k-1)
printf("%d ",s[i]+1);
else
printf("%d\n",s[i]+1);
}
}
else
{
for(int i=1; i<k2; i++)
{
if(i!=k2-1)
printf("%d ",s2[i]+1);
else
printf("%d\n",s2[i]+1);
}
}
}
return 0;
}
我想到的山不向我走来,我就向山走去的思路,其实很多时候我们都能用到, 即换位思考,逆向思维,但是在实际应用中,应该要灵活使用才好。