二分第一弹
PTAA1085/B1030完美数列
第一次做的时候没有考虑二分,暴力两次迭代果然有一个测试点卡住了,这题思路不是很难,我的二分写的应该有点问题,不过因为测试点不多,所以AC了,有空再回来看看,改一下
附上有点菜的二分AC代码:
//A1085
#include<bits/stdc++.h>
using namespace std;
long long int num[110000];
int n;
long long p;
int f(int low,int high,long long a){
if(high<=low){
return high;
}
if(num[high]<=a)return high;
int mid=(high+low)/2;
if(num[mid]>a)return f(low,mid-1,a);
else return f(mid+1,high,a);
}
int main(){
scanf("%d%lld",&n,&p);
for(int i=0;i<n;i++){
scanf("%lld",&num[i]);
}
//找到一个序列,里面最大值<=最小值*p
int ans=0;
sort(num,num+n);
int j;
//会运行超时:需要找到最大的a[i]*p>=a[j]的j值
for(int i=0;i<n;i++){
j=f(i,n-1,p*num[i]);
j=num[j]<=num[i]*p?j:j-1;
ans=max(ans,j-i+1);
}
printf("%d",ans);
return 0;
}
二分第二弹
PTA甲级在火星购物1044
先附上1/2的错误代码,用了暴力的方法,果然报错主要是没有想好二分如何实现这个题目,太菜了5555
//1044
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=110001;
int coin[maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&coin[i]);
}
int i,j;
int sum=0;
//这边可能时间比较长 :如果没有解决方案则输出最小的几串作为答案
int mina=99999999;
bool flag=false;
for(i=1;i<=n;i++){
sum=coin[i];
if(sum>m)continue;
if(sum==m){
flag=true;
printf("%d-%d\n",i,i);
continue;
}
if(i==n)break;
for(j=i+1;j<=n;j++){
if(j>n)break;
sum+=coin[j];
if(sum>m){
mina=min(mina,sum);
break;
}
if(sum==m){
flag=true;
printf("%d-%d\n",i,j);
break;
}
}
}
//
if(!flag){
m=mina;
for(i=1;i<=n;i++){
sum=coin[i];
if(sum>m)continue;
if(sum==m){
flag=true;
printf("%d-%d\n",i,i);
continue;
}
if(i==n)break;
for(j=i+1;j<=n;j++){
if(j>0)break;
sum+=coin[j];
if(sum>m){
mina=min(mina,sum);
break;
}
if(sum==m){
flag=true;
printf("%d-%d\n",i,j);
break;
}
}
}
}
return 0;
}
接下来是考虑到超时问题后的改进~
- 主要是引入sum[k]数组来保存前1-k的总价钱(突破口)
- 一开始没想好怎么二分(一直纠结二分怎么找到比原数组大的最小的那个值),后来改了一下思路,套用二分模板,很香~是我想复杂了(也可能是昨天早上太困了哈哈哈)
代码见下:
//A1044
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=101010;
int coin[maxn];
int sum[maxn];
int binary_search(int low,int high,int x){
if(low>=high)return low;
int mid=(low+high)/2;
if(sum[mid]==x)return mid;
else if(sum[mid]<x)return binary_search(mid+1,high,x);
else return binary_search(low,mid-1,x);
}
int main(){
scanf("%d%d",&n,&m);
sum[0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&coin[i]);
sum[i]=sum[i-1]+coin[i];
}
//二分
int next;
int mina=99999999;
int x;
bool flag=true;
for(int i=1;i<=n;i++){
x=m-coin[i]+sum[i];
next=binary_search(i,n,x);
if(x==sum[next]){
flag=false;
printf("%d-%d\n",i,next);
}else if(x<sum[next]){
mina=min(sum[next]-sum[i]+coin[i],mina);
}
}
if(flag){
m=mina;
for(int i=1;i<=n;i++){
x=m-coin[i]+sum[i];
next=binary_search(i,n,x);
if(x==sum[next]){
printf("%d-%d\n",i,next);
}
}
}
return 0;
}
二分第三弹
pta 甲级1010
这个测试点有点多呀:这个解法有两个测试点错误,昂
//1010
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
char n1[maxn];
char n2[maxn];
int tag,radix;
int a1,a2;
int char2num(char a){
if(a<='z'&&a>='a')return a-'a'+10;
else return a-'0';
}
int change(char ch[],int radix){
//有问题
int sum=0;
for(int i=0;i<strlen(ch);i++){
if(char2num(ch[i])>=radix)return -1;//改造失败
sum=sum*radix+char2num(ch[i]);
}
return sum;
}
int ans=1;
int main(){
scanf("%s%s%d%d",&n1,&n2,&tag,&radix);
if(tag==1){
a1=change(n1,radix);
while(ans<1137){
a2=change(n2,ans);
//printf("a1=%d a2=%d\n",a1,a2);
if(a1==a2){
printf("%d",ans);
return 0;
}else if(a2>a1){
printf("Impossible");
return 0;
}
ans++;
}
}else{
a2=change(n2,radix);
while(ans<1137){
a1=change(n1,ans);
//printf("a1=%d a2=%d\n",a1,a2);
if(a1==a2){
printf("%d",ans);
return 0;
}else if(a1>a2){
printf("Impossible");
return 0;
}
ans++;
}
}
return 0;
}
二分的题目没有用二分肯定会报超时,55.然后题目没说俺也没有注意到要用LONGLONG 类型的这整数嘤嘤嘤~~~
没有精神了,明天清醒的时候补一下~
二分确实练得比较少【忏悔】
然后结合《算法笔记》改了一下,报错更多了呜呜呜,不知道二分错在哪里:期待有大佬帮我康康~
//1010
#include<bits/stdc++.h>
using namespace std;
const int maxn=101;
typedef long long LL;
char n1[maxn];
char n2[maxn];
char temp[maxn];
int tag;
LL radix,a1,a2,ans;
LL char2num(char ch){
if(ch<='z'&&ch>='a'){
return LL(ch-'a'+10);
}else{
return LL(ch-'0');
}
}
LL change(char ch[],LL radix){
LL sum=0;
for(int i=0;i<strlen(ch);i++){
sum=sum*radix+char2num(ch[i]);
}
if(sum<0)return -1;
return sum;
}
LL binary_search(LL low,LL high,LL a1,char n2[]){
if(low>high)return -1;
LL mid=(high+low)/2;
a2=change(n2,mid);
//printf("a2=%lld,mid=%lld\n",a2,mid);
if(a1==a2)return mid;
if(a1>a2)return binary_search(mid+1,high,a1,n2);
return binary_search(low,mid-1,a1,n2);
}
LL binarysearch(char n2[],LL left,LL right,LL a1){
LL mid;
while(left<=right){
mid=(left+right)/2;
a2=change(n2,mid);
if(a1==a2)return mid;
if(a1<a2)return binarysearch(n2,left,mid-1,a1);
else return binarysearch(n2,mid+1,right,a1);
}
return -1;
}
int main(){
scanf("%s%s%d%lld",&n1,&n2,&tag,&radix);
if(tag==2){
strcpy(temp,n2);
strcpy(n2,n1);
strcpy(n1,temp);
}
a1=change(n1,radix);//a1为目标
LL low=0;
for(int i=0;i<strlen(n2);i++){
low=max(low,char2num(n2[i]));
}
low++;//底线
LL high=max(a1+1,low+1);
//printf("low=%lld high=%lld\n",low,high);
//开始二分搜索
LL ans;
ans=binarysearch(n2,low,high,a1);
if(ans<0){
printf("Impossible");
return 0;
}else{
printf("%lld",ans);
}
return 0;
}
破案了,原来是没有注意到溢出的问题;
第一版没有用二分,依次遍历的话不会有溢出的问题,所以过得测试点比较多但是会超时;
引入二分之后虽然解决了超时的问题,但是搜索空间不再从小往大,易于出现溢出的问题,这样第二版代码中直接判断a1与a2的关系就不适用了,需要另外加上判断条件
正确代码如下:
//1010
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int tag;
char N1[20];
char N2[20];
char temp[20];
LL radix;
LL ans,a1,a2;
LL inf=(1LL<<63)-1;
//将字符转换成对应的数字
LL char2num(char ch){
if(ch<='z'&&ch>='a'){
return LL(ch-'a'+10);
}else{
return LL(ch-'0');
}
}
//将字符串转换成对应redix的十进制
LL change(char ch[],LL radix,LL t){
LL sum=0;
for(int i=0;i<strlen(ch);i++){
sum=sum*radix+char2num(ch[i]);
if (sum>t||sum<0)return -1;//如果溢出或者超过N1的十进制
}
return sum;
}
//N2的十进制与a1的比较
int cmp(char N2[],LL radix,LL a1){
int len=strlen(N2);
LL num=change(N2,radix,a1);
if(num<0)return 1;//n2>n1
if(a1>num)return -1;
else if(a1==num)return 0;
else return 1;
}
//找到最大的
LL findmax(char ch[]){
LL ans=0;
for(int i=0;i<strlen(ch);i++){
ans=max(ans,char2num(ch[i]));
}
return ans;
}
//二分法
LL binary_search(LL low,LL high,LL a1,char N2[]){
while(low<=high){
LL mid=(low+high)/2;
// a2=change(N2,mid,a1);
//if(a1==a2)return mid;
// if(a1<a2)return binary_search(low,mid-1,a1,N2);
//else return binary_search(mid+1,high,a1,N2);
int flag=cmp(N2,mid,a1);
if(flag==0)return mid;
if(flag>=1){
high=mid-1;
}else{
low=mid+1;
}
}
return -1;
}
int main(){
scanf("%s%s%d%lld",&N1,N2,&tag,&radix);
if(tag==2){
strcpy(temp,N1);
strcpy(N1,N2);
strcpy(N2,temp);
}
a1=change(N1,radix,inf);
LL low=findmax(N2)+1;
LL high=max(low+1,a1+2);
ans=binary_search(low,high,a1,N2);
if(ans<0){
printf("Impossible");
}else{
printf("%lld",ans);
}
return 0;
}
我估计这题不看题解我永远不会知道自己die在溢出这个基本问题上的(暴风哭泣