1.航班时间
来源:第九届蓝桥杯省赛C++A组
题目链接
题解:因为时差的原因,来回的两地的时间差是不相同的,存在一个时间大于另一个时间,因为里向西飞要减时差,向东飞要加时差,这样用大的时间差减小的时间差就能得到2倍的时差,也就能得到飞行时间
c++代码
#include<iostream>
using namespace std;
int getse(int h,int m,int s){
return h*3600+m*60+s;}
int gettime()
{
string line;
getline(cin,line);
if(line.back()!=')')line+="(+0)";//判断末尾是否有异日情况 无则加上(+0)方便格式化算时间
int h1,h2,m1,m2,s1,s2,d;
sscanf(line.c_str(),"%d:%d:%d %d:%d:%d (+%d)",&h1,&m1,&s1,&h2,&m2,&s2,&d);//c_str 放入string字符串 实际是string 到const char* 同时我们读入起降两个时间 和 day
return getse(h2,m2,s2)-getse(h1,m1,s1)+d*3600*24;
}
int main()
{
int n;
cin>>n;
getchar();//吞掉int 与 string 中间的回车
while(n--)
{
int time =(gettime()+gettime())/2;
printf("%02d:%02d:%02d\n",time/3600,time%3600/60,time%60);//02d没有两位则用0补足
}
return 0;
}
java代码:
public class Main {
static BufferedReader buf=new BufferedReader(new InputStreamReader(System.in));
private static int get_second(int h,int m,int s)
{
return 3600*h+m*60+s;
}
private static int get_time() throws IOException
{
String str=buf.readLine();
int day=0;
if(str.charAt(str.length()-1)==')')
{
day=Integer.parseInt(""+str.charAt(str.length()-2));
str=str.substring(0, str.length()-5);
}
String s[]=str.split(" ");
String s1[]=s[0].split(":");
String s2[]=s[1].split(":");
int a1=Integer.parseInt(s1[0]);
int a2=Integer.parseInt(s1[1]);
int a3=Integer.parseInt(s1[2]);
int b1=Integer.parseInt(s2[0]);
int b2=Integer.parseInt(s2[1]);
int b3=Integer.parseInt(s2[2]);
return get_second(b1, b2, b3)-get_second(a1, a2, a3)+day*24*3600;
}
public static void main(String args[]) throws IOException
{
int n=Integer.parseInt(buf.readLine());
while(n--!=0)
{
int time=(get_time()+get_time())/2;
int hour=time/3600;
int minute=time%3600/60;
int secound=time%60;
System.out.printf("%02d:%02d:%02d", hour,minute,secound);
System.out.println();
}
}
}
2.特别数的和
来源:第十届蓝桥杯省赛C++B组
题目链接
题解:既然要判断是否存在2019中的数,我们就把范围类所有数都枚举一遍,并且单独找出他们每个位上的数判断是否满足条件就可以了;
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
int ans = 0;
for(int i = 1; i <= n; i ++){
int x = i;
while(x){
int t = x % 10;//取出x的每一位
x /= 10;
if(t == 2 || t == 0 || t == 1 || t == 9){
res += i;
break;
}
}
}
cout << ans << endl;
}
java:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int sum = 0;
for(int i = 1; i <= n ;i++) {
if(check(i)) {
sum+=i;
}
}
System.out.println(sum);
}
private static boolean check(int i) {
while(i!=0) {
if(i%10==2||i%10==9||i%10==1||i%10==0) {
return true;
}
i/=10;
}
return false;
}
}
3.k倍区间
来源:第八届蓝桥杯省赛C++B组
题目链接
题解:这题大致思路用的应该是前n项和
cin>>a[1];
long x;
for(int i=2;i<=n;i++)
{
cin>>x;
a[i]=x+a[i-1];
}
long ans=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
if((a[j]-a[i])%k==0)
ans++;
}
核心代码如上,但其复杂度是n!,所以肯定超了,我们就要想想怎么优化,(a[j]-a[i])%k可以等价于a[j]%k-a[i]%k=0,也就是2个前n项和相等,我们就可以用这个进行优化,具体细节代码里讲解;
c++代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100000+10;
long a[N];
int res[N];
int main()
{
int n,k;
cin>>n>>k;
cin>>a[1];
long x;
for(int i=2;i<=n;i++)
{
cin>>x;
a[i]=x+a[i-1];//计算前缀和
}
long ans=0;
for(int i=1;i<=n;i++)
{
int v=a[i]%k;
ans+=res[v];//用res存余数为v的个数,res[v]就表示在i之前有res[v]个余数为v的项,i和这些都可以组成一个k倍区间
res[v]++;//加上现在这个余数为v的项
}
cout<<ans+res[0]<<endl;//为什么要加一个res[0],因为上面统计是通过2个区间相减满足k倍区间的数量,但对于余数为0的前n项和,其本身就是个k倍区间不需要和其他区间相减
}
java代码:
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n,k;
n=sc.nextInt();
k=sc.nextInt();
final int N=100000+30;
long a[]=new long[N];
a[1]=sc.nextInt();
long x;
long res[]=new long[N];
Arrays.fill(res,0);
for(int i=2;i<=n;i++) {
x=sc.nextLong();
a[i]=x+a[i-1];//计算前缀和
}
long ans=0;
for(int i=1;i<=n;i++)
{
int v=(int)(a[i]%k);
ans+=res[v];//用res存余数为v的个数,res[v]就表示在i之前有res[v]个余数为v的项,i和这些都可以组成一个k倍区间
res[v]++;//加上现在这个余数为v的项
}
System.out.println(ans+res[0]);//为什么要加一个res[0],因为上面统计是通过2个区间相减满足k倍区间的数量,但对于余数为0的前n项和,其本身就是个k倍区间不需要和其他区间相减
}
}
4.整数拼接
来源:第十一届蓝桥杯省赛C++B组
题目链接
题解:出处(https://www.acwing.com/solution/content/15969/)
补充:从前往后跑一遍就是用现在这个数和在这个数前面的数组合,从后往前就是用这个数和这个数后面的数组合,以此做到不从不漏,并且不用判断是否为同一个数
c++代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef unsigned long long ll;
const int N = 100010;
ll n, mod;
ll ans;
ll a[N];
int cnt[11][N];
int log(int x)
{
int res = 0;
while (x)
x /= 10, res++;
return res;
}
void get()
{
for (int i = 0; i < n; i++)
{
int v = (mod - a[i] % mod) % mod;//为什么补了%mod 如果前面的数小于mod 外面的mod没有影响,如果a[i]%mod=0 那么如果不补的话v就等于mod了,实际应该为0所以补一个%mod来消除这种特殊情况
ans += cnt[log(a[i])][v];
for (ll j = 0, power = 1; j < 11; j++)
{
cnt[j][power * a[i] % mod]++;
power = power * 10;
}
}
}
int main()
{
scanf("%d%d", &n, &mod);
for (int i = 0; i < n; i++)
scanf("%d", a + i);
get();//从前往后
memset(cnt, 0, sizeof cnt);
reverse(a, a + n);
get();//从后往前
printf("%lld\n", ans);
return 0;
}
java代码:
import java.util.Arrays;
import java.util.Scanner;
public class Main {
final static int N=100110;
static int [][]cnt=new int[11][N];
static int n,mod;
static long []a=new long [N];
static long ans=0;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
mod=sc.nextInt();
for(int i=1;i<=n;i++)
a[i]=sc.nextLong();
for(int i=1;i<11;i++)
Arrays.fill(cnt[i],0);
get();
for(int i=1;i<11;i++)
Arrays.fill(cnt[i],0);
for(int begin=1,end=n;begin<end;begin++,end--)
{
long x=a[begin];
a[begin]=a[end];
a[end]=x;
}
get();
System.out.println(ans);
}
static void get()
{
for (int i = 1; i <= n; i++)
{
int v =(int) (mod - a[i] % mod) % mod;//为什么补了%mod 如果前面的数小于mod 外面的mod没有影响,如果a[i]%mod=0 那么如果不补的话v就等于mod了,实际应该为0所以补一个%mod来消除这种特殊情况
ans += cnt[log(a[i])][v];
int power=1;
for (int j = 0; j < 11; j++)
{
cnt[j][(int)(power * a[i] % mod)]++;
power = power * 10%mod;
}
}
}
static int log(long x) // 返回 log10(x)
{
int res = 0;
while (x>0) {
x /= 10;
res ++ ;
}
return res;
}
}