蓝桥杯(2019年真题)

后缀表达式(思维)

题意: 给出加号和减号个数n,m,以及n+m+1个数,问后缀表达式的最大值是多少。
题解: 加号一般不会有变化,主要是减号可以使负数变正,
1、减号个数为0,那么结果就是所有数的和
2、减号个数不为0:
①、负数个数为0,减去最小的正数例如n=1,m=2, 1 2 3 4 就可以组成4+3-(1-2)
②、负数个数不为0:
a、负数个数==m+n+1,那么肯定有一个负数无法变正,就让最大的负数不为正其他负数为正即可
b、负数个数!=m+n+1,那么肯定能将所有的负数变成正数

package bag;
import java.util.*; 
public class Main {
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		int n=sc.nextInt() ;
		int m=sc.nextInt() ; 
		long[] a = new long[n+m+1] ; 
		int cnt=0 ; 
		long sum=0 ; 
		for(int i=0 ; i<n+m+1 ; ++i) {
			a[i]=sc.nextInt() ; 
			if(a[i]<0)	++cnt ;
			sum += a[i] ; 
		}
		Arrays.sort(a,0,m+n+1);
		if(m==0)	System.out.println(sum);
		else {
			 if(cnt>0) {
				 if(cnt!=n+m+1) {
					 for(int i=0 ; i<cnt ; ++i)	sum-=2*a[i] ;
				 }
				 else	
					 for(int i=0 ; i<cnt-1 ; ++i)	sum-=2*a[i] ; 
			 }
			 else	sum -= 2*a[0] ; 
			 System.out.println(sum);
		}
	}
}

糖果(状压dp)

题意: 现有n包糖果,每包糖果内有k颗糖果(给出糖果编号),小明要收集m种糖果,最少要买几包糖果,如果不能全部收集则输出-1
输入n,m,k ,然后是n包糖果内的糖果编号
样例输入:
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
样例输出:2
题解: 因为m的范围很小1~20 ,所以考虑状态压缩,将糖果编号转化为二进制,在有糖果的数位上标上1,然后将二进制转化为十进制,例如样例:
1 1 2 -> 0 0 0 1 1 (3)
1 2 3 -> 0 0 1 1 1 (7)
1 1 3 -> 0 0 1 0 1 (5)
2 3 5 -> 1 0 1 1 0 (22)
5 4 2 -> 1 1 0 1 0 (26)
5 1 2 -> 1 0 0 1 1 (19)
数组c[]为转化后的十进制数,dp[i]表示i的糖果组合最少需要买几包糖果,初始dp[c[i]]=1(就是上面转化后的十进制数),然后从1~(1<<m)遍历
两种状态更新 (前提j的组合情况是存在的 dp[j]!=0):
dp[j|c[i]] == 0 说明 j|c[i] 这种组合情况是没有出现过的可以更新,
dp[j|c[i]] > dp[j]+1 ,说明j|c[i] 这种组合需要买的糖果包数多了,可以以更少的糖果包数组合,所以也要更新

import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		int n = sc.nextInt() ; 
		int m = sc.nextInt() ; 
		int k = sc.nextInt() ; 
		int[] c = new int[1<<21] ; 
		int[] dp = new int[1<<21] ; 
		for(int i=0 ; i<n ; ++i) {
			for(int j=0 ; j<k ; ++j) {
				int a = sc.nextInt() ;
				c[i] |= 1<<(a-1) ; 
			}
//			System.out.println("i="+i+" "+c[i]);
			dp[c[i]]=1 ; 
		}
		for(int i=0 ; i<n ; ++i) {
			for(int j=0 ; j<=(1<<m) ; ++j) {
				if(dp[j]==0) continue ; 
				if(dp[j|c[i]]==0 || dp[j|c[i]]>dp[j]+1)
					dp[j|c[i]]=dp[j]+1 ; 
			}
		}
		if (dp[(1<<m)-1]!=0)		System.out.println(dp[(1<<m)-1]);
		else	System.out.println("-1");
	}
	
}

外卖的优先级(模拟)

题意: 给定n,m,T,n个店铺,m个订单,每个订单包含订单到达的时间ts和店铺的id,每家外卖店都有 一个优先级,初始时 (0 时刻) 优先级都为 0。
每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减 到 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2。
题解: 先按id,ts升序排序,然后模拟过程即可,但是有几个点药注意。
(这里用times记录店铺的优先级),在增加优先级之前要先进行店铺是否退出缓存的判断,即 if(times<=3), 因为在此前面进行了该时间点到上一个时间点的优先级减少,比如有可能优先级已经减少到了2(退出缓存),但是如果增加优先级+2 再进行判断就是 4>3 ,但实际上在收到该订单之前已经退出了缓存。第二个要注意的点就是在结束的时候用 优先级-T-最后订单的时间,然后判断是否退出缓存。

package race;
import java.util.*;
public class Main {
	static class node{
		int t,id ; 
		public node(int a,int b) {t=a;id=b;} 
	}
	static Comparator<node> cmp = new Comparator<node>(){
		public int compare(node a,node b) {
			if (a.id==b.id)		return a.t-b.t ; 
			return a.id-b.id ; 
		}
	} ;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		int n = sc.nextInt() ; 
		int m = sc.nextInt() ;
		int T = sc.nextInt() ; 
		node[] s = new node[m+1] ; 
		for(int i=0 ; i<m ; ++i) {
			int t=sc.nextInt() ;
			int id=sc.nextInt() ; 
			s[i] = new node(t,id) ; 
		}
		Arrays.sort(s,0,m,cmp);		//按id优先然后时间升序排序
		int cnt=0 ; //记录在T时在缓存的店铺数目
		int times=0,j=0 ; //店铺优先级
		for(int i=1 ; i<=n&&j<m ; ++i) {
			if(s[j].id!=i)	continue ; 
			times=0 ;
			int last=0 ; 
			boolean flag=true ,in=false; //是否为当前店铺的第一个订单
			while(j<m&&s[j].id==i) {
				if (flag) {
					times=2; 
					flag=false ; 
				}
				else {
					if (s[j].t-s[j-1].t>1)	times-=s[j].t-s[j-1].t-1 ; 
					if(times<0)		times=0 ; 
					//判断不在缓存中要在+=2之前判断,因为有可能在这个时间点还没接收到订单之前已经退出了缓存
					if(times<=3)		in=false; 
					times+=2 ; 
					if(times>5)	 		in=true ; 
				}
				++ j ;
			}
			times -= T-s[j-1].t ; //最后时间点和给定时间点的差值
			if(times<=3)	in=false ; 		
			if(in)	++cnt; 
		}
		System.out.println(cnt);
	}
	
}

灵能传递(JavaB组J题 思维难题)

参考博客
讲解视频

package race;
import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		int t = sc.nextInt() ; 
		while(t>0) {
			int n = sc.nextInt() ; 
			long[] s = new long[n+1] ;
			long[] st = new long[n+1] ;
			boolean[] vis = new boolean[n+1] ; 
			Arrays.fill(vis,false) ;
			s[0]=0 ; 
			for(int i=1 ; i<=n ; ++i) {
				s[i] = sc.nextLong() ; 
				s[i] += s[i-1] ; 
			}
			long s0=s[0],sn=s[n] ; 
			if(s0>sn) {long x=s0;s0=sn;sn=x;}
			Arrays.sort(s,0,n+1);
			for(int i=0 ; i<=n ; ++i) {
				if(s0==s[i]) {
					s0=i ;
					break ;
				}
			}
			for(int i=n ; i>=0 ; --i) {
				if(sn==s[i]) {
					sn=i ; 
					break ; 
				}
			}
			int l=0,r=n ; 
			for(int i=(int)s0 ; i>=0 ; i-=2) {
				st[l++] = s[i] ; 
				vis[i]=true ; 
			}
			for(int i=(int)sn ; i<=n ; i+=2) {
				st[r--] = s[i] ; 
				vis[i]=true ; 
			}
			for(int i=0 ; i<=n ; ++i) {
				if(vis[i]==false)
					st[l++]=s[i] ; 
			}
			long ans=0 ; 
			for(int i=1 ; i<=n ; ++i) {
				ans = Math.max(ans,Math.abs(st[i]-st[i-1])) ; 
			}
			System.out.println(ans);
			--t ; 
		}
	}
	
}

人物性格分析(模拟)

题意: 给出一串字符串和k,若Alice和Bob之间的字符不超过k,则说明同时出现,询问Alice和Bob同时出现的次数。
题解:
1、输入带有空格字符串使用nextLine() ,前面的k输入后要nextLine()一行才能接收下面的字符串。
2、拆分出字符串的单词,使用字符串的split方法

split(), 括号内写用来分割字符串的字符,这里应该用空格和.分割,使用两种字符分割的时候用 | 来连接,还有要注意的点是 使用 . 要用转义字符,因此是 \.

更多split用法
3、遍历word数组,记录Alice和Bob出现的位置
*4、最最容易超时的点,通过记录的位置找两单词间隔k的个数,一开始我是两重循环寻找然后就超时了,记录的时候两个数组都是从小到大的,所以可以通过这个特性来优化,例如:先寻找Alice后面间隔k字符的Bob个数,先用l记录大于当前Alice出现的位置,r记录在Alice后面间隔k的Bob的位置,然后r-l就是该区间的Bob的数目,然后寻找Bob后面的Alice也是同理。

package race;
import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		int k = Integer.parseInt(sc.nextLine()) ;
		String s = sc.nextLine() ;
		String[] word = s.split(" |\\.") ; 
		int cur=0;
		long cnt=0 ; 
		int[] a = new int[1000000] ;
		int[] b = new int[1000000] ; 
		int apos=0 , bpos=0 ; 
		for(int i=0 ; i<word.length ; ++i) {//记录Alice和Bob出现的位置
			if(word[i].equals("Alice")) 	a[apos++] = cur ; 
			if(word[i].equals("Bob")) 	b[bpos++] = cur ; 
			cur += word[i].length()+1 ; 
		}
		int l=0,r=0 ; 
		for(int i=0 ; i<apos ; ++i) {
			while(l<bpos && b[l]<a[i])	++l ; 
			while(r<bpos && b[r]<=a[i]+k+5)	++r ; 
			cnt += r-l ; 
		}
		l=0 ;r=0 ; 
		for(int i=0 ; i<bpos ; ++i) {
			while(l<apos && a[l]<b[i])	++l ; 
			while(r<apos && a[r]<=b[i]+k+3)	++r ; 
			cnt += r-l ; 
		}
		System.out.println(cnt);
	}
	
}

扫地机器人(二分)

package race;
import java.util.*;
public class Main {
	static Long[] a ;
	static int n,k; 
	public static boolean check(Long x) {
//		System.out.println("check : "+x);
		long pos=0 ; 
		for(int i=1 ; i<=k ; ++i) {
			long t=0 ; 
			if(a[i]>pos)	t=x-(a[i]-pos-1)*2 ;
			else	t=x ; 
			if (t<0)	return false ; 
			pos = Math.min(t/2+a[i],a[i+1]) ; 
//			System.out.println("pos="+pos+" "+a[i+1]+" "+t);
		}
		return pos>=n ; 
	}
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		n = sc.nextInt() ;
		k = sc.nextInt() ; 
		a = new Long[k+2] ; 
		a[0]=(long)0 ; a[k+1]=(long)n ; 
		for(int i=1 ; i<=k ; ++i)	a[i]=sc.nextLong() ; 
		Arrays.sort(a,1,k+1);
		long ans=2*n , l=0,r=2*n ; 
		while(l<r) {
			long mid = (l+r)>>1 ;
			if(check(mid)) {
				ans = Math.min(ans,mid) ; 
				r=mid ; 
			}
			else	l=mid+1 ; 
		}
		System.out.println(ans);
	}
	
}

组合数问题(lucas定理+数位dp)-超时未AC

参考博文
用dfs会超时,emmmm还在研究中,先存个档吧
这个是超时的,AC代码看转载的博文。

package race;
import java.util.*;
public class Main {
	static long n,m;
	static int alen,blen,k; 
	static int[] a,b; 
	static long dp[][][][][] ; 
	static long mod=1000000007 ; 
	public static long dfs(int len,int f1,int f2,int f3,int f4) {
		//当前长度,i是否上限,j是否上限,i>j , j<i
		if(len==0)	return f4 ;
		if (dp[len][f1][f2][f3][f4]!=0)	return dp[len][f1][f2][f3][f4] ;
		long ans=0 ;
		int imax=f1==1?a[len]:k-1 , jmax=f2==1?b[len]:k-1; 
		for(int i=0 ; i<=imax ; ++i) {
			for(int j=0 ; j<=jmax; ++j) {
				if (f3==0 && i<j)	continue ; 
				ans = (ans+dfs(len-1,(f1==1&&(i==imax))?1:0,
						(f2==1&&(j==jmax))?1:0,(f3==1||(i>j))?1:0,(f4==1||i<j)?1:0))%mod ;
			}
		}
		return dp[len][f1][f2][f3][f4]=ans ; 
	}
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		int T = sc.nextInt()  ; 
		k=sc.nextInt() ; 
		while(T>0) {
			n=sc.nextLong() ;
			m=sc.nextLong() ; 
			a = new int[70] ; 
			b = new int[70] ; 
			long t=n ; 
			alen=0 ;blen=0 ; 
			while(t>0) {a[++alen]=(int)(t%k); t/=k;}
			t=m ; 
			while(t>0) {b[++blen]=(int)(t%k); t/=k;}
			dp = new long[Math.max(alen, blen)+1][2][2][2][2] ; 
			System.out.println(dfs(Math.max(alen,blen),1,1,0,0));
			--T ; 
		}
		
		
	}
	
}

RSA解密(扩展欧几里得求逆元)

在这里插入图片描述
1、先对n进行因式分解,求出q和p,计算mod=(p-1)(q-1)
2、e*d%mod = 1 , e = (1/d)%mod , 即e为d的逆元,利用扩展欧几里得求出d的逆元,要注意这里的mod不是质数所以不能用费小马定理来求得逆元
扩展欧几里得求逆元的三种方法
3、更具题意的公式求得C,但是要注意这里会爆long所以可以使用java的大数类BigInteger

package race;
import java.math.BigInteger;
import java.util.*;
public class Main {
	static long x,y ; 
	public static void exgcd(long a,long b) {
		if(b==0) {
			x=1;y=0 ;
			return ; 
		}
		exgcd(b,a%b) ; 
		long t=x ; x=y ; 
		y = t-a/b*y ; 
	}
	public static long fastpow(long n,long a,long mod) {
		long res=1 ; 
		n %= mod ;
		while(a>0) {
			if((a&1)==1)	res = res*n%mod ; 
			n = n*n%mod ; 
			a>>=1 ; 
		}
		return res ; 
	}
	static long p,q ; 
	public static void init(long n) {
		for(int i=2 ; i*i<=n ; ++i) {
			if(n%i==0&&isprime(i)&&isprime(n/i)) {
				p=i ; q=n/i ; 
				return ; 
			}
		}
	}
	public static boolean isprime(long n) {
		for(long i=2 ; i*i<=n ; ++i)
			if(n%i==0)	return false ;
		return true ; 
	}
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		long n= 1001733993063167141L ;
		long d=212353 ; 
		long c=20190324 ;
//		long p=891234941,q=1123984201 ; //q p答案
		init(n) ; //因式分解n,求p和q 
		long mod=(p-1)*(q-1) ; 
		exgcd(d,mod) ;	//扩展欧几里得求逆元
		long e=(x+mod)%mod ;
		/*	由于mod=(p-1)(q-1) 不是质数,所以不能使用费小马定理来求d的逆元
		long cur = fastpow(n,mod-2,mod) ;
		System.out.println(isprime(mod));
		System.out.println(cur);
		*/
		BigInteger ans = BigInteger.valueOf(c) ;
		BigInteger cc = BigInteger.valueOf(e) ;
		BigInteger nn = BigInteger.valueOf(n) ; 
		ans = ans.modPow(cc,nn) ; 
		System.out.println(ans);
		System.out.println(e);
	}
	
}

答案:
ans = 579706994112328949
e = 823816093931522017

C++ 版,c++没有大数类所以在快速幂的时候要用到快速乘法

#include <cstdio>
#include <algorithm>
using namespace std ;
typedef long long ll ; 
ll x,y,p,q ;  
void exgcd(ll a,ll b){
	ll t ; 
	if(b==0){
		x=1,y=0 ;
		return ;  
	}
	exgcd(b,a%b) ; 
	t=x , x=y ; 
	y=t-a/b*y ; 
}

ll fastmul(ll n,ll m,ll mod){
	ll ans=0 ; 
	while(m){
		if(m&1)		ans=(ans+n)%mod ; 
		n=(n<<1)%mod ; 
		m>>=1 ; 
	}
	return ans ;
}
ll fastpow(ll n,ll a,ll mod){
	ll res=1 ; 
	while(a){
		if(a&1)		res=fastmul(res,n,mod) ; 
		n=fastmul(n,n,mod) ; 
		a >>=1 ; 
	}
	return res ; 
}
bool isprime(ll n){
	for(ll i=2 ; i*i<=n ; ++i)
		if(n%i==0)	return false ; 
	return true ; 
}
void init(ll n){
	for(ll i=2 ; i*i<=n ; ++i){
		if(n%i==0&&isprime(i)&&isprime(n/i)){
			p=i , q=n/i ; 
			return ; 
		}
	}
}
int main(){
	ll n= 1001733993063167141,d=212353,c=20190324 ;
	init(n) ;
	ll mod=(p-1)*(q-1) ; 
	exgcd(d,mod) ; 
	ll e = (x+mod)%mod ; 
	printf ("p=%lld q=%lld\n",p,q) ; 
	printf ("e=%lld\n",e) ; 
	ll ans = fastpow(c,e,n) ; 
	printf ("ans=%lld\n",ans%n) ; 
	return 0 ; 
} 

子串数字(26进制的计算)

package race;
import java.util.*;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in) ; 
		char[] s = {'L','A','N','Q','I','A','O'} ; 
		long ans=0 ; 
		long pos=0 ,t=1; 
		for(int i=s.length-1 ; i>=0 ; --i) {
			int idx = s[i]-'A'+1 ;
			ans += t*idx ; 
			t *= 26 ; 
		}
		System.out.println(ans);
		
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值