日常orzPo爷的时候看到的。感觉挺好玩的就推了一下md发现自己智商不够。
感觉举个例子真是简单易懂啊。下面的a,b,c均表示原题中a,b,c的位数:另外不妨令a<b。另外边界什么的问题好像这道题目里面不是很关键把就不考虑了。
1.c<a<b,构造的例子如下(a=4,b=7,c=2):
0001111111
0110000011
1000000010
令f(a,b,c)表示满足c<a<b时的最小位数。当c=1的时候,显然需要a+b+1位,即f(a,b,1)=a+b+c;否则,如果末位不是(1,1),那么位数就是f(a-1,b,c-1)+1或者f(a,b-1,c-1)+1;否则位数为f(a-1,b-1,c-1)+1;那么根据数学归纳法(只能宽泛地讲一下了),一定是f(a-1,b-1,c-1)最小,因此末位只能是(1,1)
2.a<c<b,构造的例子如下(a=4,b=7,c=5):
00011110
01111111
10011101
显然位数一定>b,那么位数为b+1一定是最优的,因此只能让第二个数的b个1在最后。假设第一个数的最后一位是第k位(从低到高),那么先把第k位加入,就变成100......0010000(假设k=5),然后加入的数显然是第k~k+a-2位才能让答案最小。那么现在考虑满足c的限制,发现第三个数是这样的10......01......101......1,然后我们知道有c个1,知道最高位是b+1,然后那个单独的0的位置是第c-a+1位,就好了。
3.b<c<b+c,构造的例子如下(a=4,b=7,c=8):
011100001
011111110
111101111
同样的显然位数>c(除非a+b=c),那么位数为c+1一定最优,于是我们要让0出现的尽可能早。注意到以第二个数为基准,第一个数的开头的1的个数就是a+b-c,那么我们让开头a+b-c全都是1即可。
4.无解情况。。。。这个还用讲吗。。。
AC代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
int calc(int x){
int t=0; for (; x; x>>=1) t++; return t;
}
int getit(int x){
int t=0; for (; x; x^=x&-x) t++; return t;
}
int main(){
int a,b,c; scanf("%d%d%d",&a,&b,&c);
int len=max(max(calc(a),calc(b)),calc(c)),ans;
a=getit(a); b=getit(b); c=getit(c);
if (a>b) swap(a,b);
if (c<=a) ans=(1<<(a+b-c))|((1<<c)-2);
else if (c<=b) ans=((1<<b)|((1<<c)-1))^(1<<(c-a));
else if (c<=a+b) ans=((1<<(c+1))-1)^(1<<(c+c-a-b));
else{
puts("-1"); return 0;
}
printf("%d\n",(calc(ans)<=len)?ans:-1);
return 0;
}
by lych
2016.4.7
这样能让0