PTA写BUG日志——二分篇(算法笔记上机实战)

二分第一弹

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在溢出这个基本问题上的(暴风哭泣

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值