lqb 2017省赛题单

2017省赛 题单

填空 等差素数数列

类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。上边的数列公差为 30,长度为 6。2004 年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。 这是数论领域一项惊人的成果!有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:

长度为 10 的等差素数列,其公差最小值是多少?

  • DFS 剪枝
#include<bits/stdc++.h>
using namespace std;

bool isSs(int x){
	for(int i = 2 ; i < x ; i ++){
		if(x % i == 0) return false;
		else continue;
	}
	return true;
}
int main(){
	vector<int> ss;
	for(int i = 3 ; i < 100000 ; i ++){
		if(isSs(i)){
			for(int j = 2 ; j < 10000 ; j ++){
				int k = 0 ;
				while(k < 10){
					if(!isSs(i + k * j)) break;
					else k ++;  
				}
				if(k == 9) {
					for(int kx = 0 ; kx < 10 ; kx ++) cout << i + kx * j << " ";
					cout << endl ;
				}
				else continue;
			}
		}
		
	}
	cout << "no" << endl;
	return 0;
}

纯纯暴力很耗时间,剪枝之后搜索得很快:

17 6947 13877 20807 27737 34667 41597 48527 55457 62387
137 8117 16097 24077 32057 40037 48017 55997 63977 71957
409 619 829 1039 1249 1459 1669 1879 2089 2299           
433 3583 6733 9883 13033 16183 19333 22483 25633 28783
521 10181 19841 29501 39161 48821 58481 68141 77801 87461
937 10177 19417 28657 37897 47137 56377 65617 74857 84097
1699 5689 9679 13669 17659 21649 25639 29629 33619 37609
2063 3323 4583 5843 7103 8363 9623 10883 12143 13403
3413 8663 13913 19163 24413 29663 34913 40163 45413 50663
3499 3709 3919 4129 4339 4549 4759 4969 5179 5389
3823 6133 8443 10753 13063 15373 17683 19993 22303 24613
4091 12071 20051 28031 36011 43991 51971 59951 67931 75911
4721 7451 10181 12911 15641 18371 21101 23831 26561 29291
6043 6883 7723 8563 9403 10243 11083 11923 12763 13603
6553 14323 22093 29863 37633 45403 53173 60943 68713 76483
7151 14081 21011 27941 34871 41801 48731 55661 62591 69521

贴一个格林与华人陶哲轩合作证明的原文文献,看不懂。显然 409 为首行公差最小:210。当然也看到 6043 为首行公差也只有:840,那么如何证明 210 为最小值呢?不会证。

K倍区间

给定一个长度为 N 的数列,A1,A2,⋯AN​,如果其中一段连续的子序列 Ai​,Ai​+1,⋯Aj​ ( i≤j ) 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。

你能求出数列中总共有多少个 K 倍区间吗?

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 100010;
ll n, k, x;
ll a[maxn]; // 
int main() // 
{
    cin >> n >> k;
    ll sum = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &x);
        sum += x%k; // 取模
        sum %= k;
        a[sum]++; // 记录此模数区间的位置
    }
    if (a[0] != 0) a[0]++;
    ll ans = 0;
    for (int i = 0; i < maxn; i++) {
        if (a[i] == 0)continue;
        ans += a[i] * (a[i] - 1) / 2; //  组合数公式,C(n,2)
    }
    cout << ans << endl;
}

大数取模,不然会超限。这个没想到,得注意一下。

填空 迷宫

X 星球的一处迷宫游乐场建在某个小山坡上。它是由 10×1010 \times 1010×10 相互连通的小房间组成的。
房间的地板上写着一个很大的字母。我们假设玩家是面朝上坡的方向站立,则:

LLL 表示走到左边的房间,
RRR 表示走到右边的房间,
UUU 表示走到上坡方向的房间,
DDD 表示走到下坡方向的房间。

X 星球的居民有点懒,不愿意费力思考。他们更喜欢玩运气类的游戏。这个游戏也是如此!
开始的时候,直升机把 100100100 名玩家放入一个个小房间内。玩家一定要按照地上的字母移动。
迷宫地图如下:

UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR

请你计算一下,最后,有多少玩家会走出迷宫,而不是在里边兜圈子?

  • DFS
#include<bits/stdc++.h>
using namespace std;

int ans = 0 ;
bool visit[11][11] = {0} ;
string s[11] ;

void dfs(int i , int j){
	if(visit[i][j] == true) return ;
	if(i >= 10 || i < 0 || j >= 10 || j < 0) {
		ans ++ ;
		return ;
	}
	if(s[i][j] == 'U') {
		visit[i][j] = true ;
		dfs(i-1,j) ;
		visit[i][j] = false ;
	}
	if(s[i][j] == 'D') {
		visit[i][j] = true ;
		dfs(i+1,j) ;
		visit[i][j] = false ;
	}
	if(s[i][j] == 'L') {
		visit[i][j] = true ;
		dfs(i,j-1) ;
		visit[i][j] = false ;
	}
	if(s[i][j] == 'R') {
		visit[i][j] = true ;
		dfs(i,j+1) ;
		visit[i][j] = false ;
	}
}

int main(){
	for(int i = 0 ; i < 10 ; i ++){
		getline(cin,s[i]) ;
	}
	for(int i = 0 ; i < 10 ; i ++){
		for(int j = 0 ; j < 10 ; j ++){
			dfs(i,j);
		}
	}
	cout << ans ;
	return 0 ;
}

分巧克力

儿童节那天有 K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有 N 块巧克力,其中第 i 块是 Hi​×Wi 的方格组成的长方形。为了公平起见,
小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。切出的巧克力需要满足:

形状是正方形,边长是整数; 大小相同;

例如一块 6x5 的巧克力可以切出 6 块 2x2 的巧克力或者 2 块 3x3 的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

  • 贪心
#include<bits/stdc++.h>
using namespace std;

int n , k , MaxLength = 0 , cnt = 0 , ans = 0 ;
int h[100010] ;
int w[100010] ;

int main(){
	cin >> n >> k ;
	for(int i = 0 ; i < n ; i ++){
		cin >> h[i] >> w[i] ;
		MaxLength = max(MaxLength , min(h[i] , w[i])) ;
	}
	
	for(int i = MaxLength ; i > 0 ; i --){
		for(int j = 0 ; j < n ; j ++){
			int hcnt = h[j] / i ;
			int wcnt = w[j] / i ;
			cnt += hcnt * wcnt ;
		}
		if(cnt >= k) {
			cout << i << endl;
			break ;
		}
		else cnt = 0 ;
	}
	
	return 0 ;
}

第一次MaxLength = max(MaxLength , min(h[i] , w[i]))这里是写做MaxLength = min(MaxLength , min(h[i] , w[i])),然而注意到并不是每一块巧克力都得用到,所以从最大的短边开始遍历。

  • 二分优化
#include <bits/stdc++.h>
using namespace std;

const int def=100005;
int h[def]={0};
int w[def]={0};
int n,k,MaxLength = 0;

bool check(int x) //判断边长为x的正方形份数是否够
{
  int sum = 0;
  for(int i = 0 ; i < n ; i ++)
  {
    sum += (h[i] / x) * (w[i] / x);
  }
  if(sum>=k) return true;
  else return false;
}
int main()
{
  cin >> n >> k;
  for(int i = 0 ; i < n ; i ++)
  {
    cin >> h[i] >> w[i];
    MaxLength = max(MaxLength , min(h[i] , w[i])) ;
  }
  //二分法判断边长
  int l = 1;
  int r = MaxLength;
  int max = 0;
  while(l <= r)
  {
    int mid = (l+r) >> 1 ;
    if(check(mid)) //mid边长是满足的
    {
      max = mid ;
      l = mid + 1 ; //要求最大边长,还需要继续判断
    }
    else
    r = mid - 1 ; //说明不满足,需要缩减边长
  }
  cout<<max;
  return 0;
}

理论上应该会更快,实际上没有看出来。可能数据量还有达到某个值。

填空 贪吃蛇长度

+-------------------------------------------------+
|                                                 |
|    H######                      ####            |
|          #                      #  #            |
|          #                      #  #            |
|          #     ####             #  #            |
|          #     #  #             #  #            |
|          ######@###             #  #            |
|                #       ####     #  #            |
|                #       #  #     #  #            |
|            ####@#######@###     #  #            |
|            #   #       #        #  #            |
| T          #####       #        #  #   ##       |
| #                      #      ###  ### ##       |
| ################       #      #      ####       |
|                #       #      #         #       |
|   ##############       #######@##########       |
|   #                         ###                 |
|   ###########################                   |
+-------------------------------------------------+

小明在爷爷的私人收藏馆里找到一台老式电脑。居然没有图形界面,只能用控制台编程。 经过小明的一阵摸索,神奇地设计出了控制台上的贪食蛇游戏。
如上,是游戏时画面截图。
其中,H 表示蛇头,T 表示蛇尾。# 表示蛇的身体,@ 表示身体交叉重叠的地方。 你能说出现在的贪吃蛇长度是多少吗?

其实,只要数出 # 的数目算 1,数出 @的数目算的数目算 的数目算 2,再加上头尾各算 ,再加上头尾各算 ,再加上头尾各算1 就计算好了。

人工数一下?太累眼睛了,聪明的你为什么不让计算机帮忙呢?
本题的要求就是: 请输出上图中贪食蛇的长度是多少?

  • 简单模拟
#include<bits/stdc++.h>
using namespace std;


int main(){
	string s[25] ;
	int ans = 0 ;
	for(int i = 0 ; i < 21 ; i ++){
		getline(cin , s[i]) ;
	}
	for(int i = 0 ; i < 21 ; i ++){
		for(int j = 0 ; j < s[i].size() ; j ++){
			if(s[i][j] == '#') ans ++ ;
			if(s[i][j] == '@') ans += 2 ;
		}
	}
	
	ans += 2 ; // head tail
	
	cout << ans << endl ;
	
	return 0 ;
}

正则问题

考虑一种简单的正则表达式:

只由 x ( ) | 组成的正则表达式。

小明想求出这个正则表达式能接受的最长字符串的长度。
例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是 6。

  • DFS
#include<bits/stdc++.h>
using namespace std;

string s ;
int i = -1 ;

int dfs(){
	int sum = 0 , cnt = 0 ;
	while(i < (int)s.size()){
        i ++ ;
        int nowi = i ;
		if(s[i] == 'x') {
			cnt ++ ;
		}
		else if(s[i] == '|') {
			sum = max (cnt , sum) ;
			cnt = 0 ;
		}  
		else if(s[i] == '(') {
			cnt += dfs() ;
            
		}
		else if(s[i] == ')') {
			break ;
		}
	}
	return max(sum , cnt) ;
}

int main(){
	getline(cin , s) ;
	int ans = dfs() ; 
	cout << ans << endl ;
	
	return 0 ;
}

注意 while 内要用 if else if 不能并列四个 if ,,不然只要遇到一个 )就卡住出不来了。或者 ) 要放在判断语句的前头,在每一次出( 时不会刚好卡在 )里。

填空 取位数

求1个整数的第k位数字有很多种方法。 以下的方法就是一种。

  • 2min 膨胀
#include <stdio.h>
// 求x用10进制表示时的数位长度 
int len(int x){
    if(x<10) return 1;
    return len(x/10)+1;
}
    
// 取x的第k位数字
int f(int x, int k){
    if(len(x)-k==0) return x%10;
    return f(x/10,k);  //填空
}
    
int main()
{
    int x = 23574;
    printf("%d\n", f(x,3));
    printf("%d\n", f(893275,2));
}

日期问题

小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在 1960 年 1 月 1 日至 2059 年 12 月 31 日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。
更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。
比如 02/03/04,可能是 2002 年 03 月 04 日、2004 年 02 月 03 日或 2004 年 03 月 02 日。

给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

  • 去他妈的模拟
#include <bits/stdc++.h>
using namespace std;

bool is31(char a, char b){
	if(a == '0' && b >= '1' && b <= '9') return true ;
	if(a >= '1' && a <= '2' && b >= '0' && b <= '9') return true ;
	if(a == '3' && b >= '0' && b <= '1') return true ;
	return false ;
}

bool is30(char a, char b){
	if(a == '0' && b >= '1' && b <= '9') return true ;
	if(a >= '1' && a <= '2' && b >= '0' && b <= '9') return true ;
	if(a == '3' && b == '0') return true ;
	return false ;
}

bool is28(char a, char b){
	if(a == '0' && b >= '1' && b <= '9') return true ;
	if(a == '1' && b >= '0' && b <= '9') return true ;
	if(a == '2' && b >= '0' && b <= '8') return true ;
	return false ;
}

bool is29(char a, char b){
	if(a == '0' && b >= '1' && b <= '9') return true ;
	if(a == '1' && b >= '0' && b <= '9') return true ;
	if(a == '2' && b >= '0' && b <= '9') return true ;
	return false ;
}

bool isRYear(char a, char b){
	int x = a - '0' , y = b - '0' ;
	int ye = x * 10 + y ;
	if(x < 6) ye += 2000 ;
	else ye += 1900 ;
	ye -= 2000 ;
    if(ye % 4 == 0) return true ;
	else return false ;
}

bool FUCK(char a, char b, char c, char d, char e, char f){ // month-ab day-cd year-ef
	if(a == '0' && (b == '1' || b == '3' || b == '5' || b == '7' || b == '8' ) && is31(c,d)) return true ;
	if(a == '1' && (b == '0' || b == '2') && is31(c,d)) return true ;
	if(a == '0' && b == '2' && !isRYear(e,f) && is28(c,d)) return true ;
	if(a == '0' && b == '2' && isRYear(e,f) && is29(c,d)) return true ;
	if(a == '0' && (b == '4' || b == '6' || b == '9') && is30(c,d)) return true ;
	if(a == '1' && b == '1' && is30(c,d)) return true ;
	return false ;
}

string Year(char a, char b){
	string ans ;
	if(a >= '0' && a <= '5'){
		ans = "20" ;
		ans.push_back(a) ;
		ans.push_back(b) ;
	}
	else{
		ans = "19" ;
		ans.push_back(a) ;
		ans.push_back(b) ;
	}
	return ans ;
}

bool isSame(char a, char b, char c, char d){
	if(a == c && b == d) return true ;
	else return false ;
}

int main() { // day 01-31 month 01-12 year 00-99
	string s , ans ;
	cin >> s ;
	if( FUCK(s[3],s[4],s[6],s[7],s[0],s[1]) ){ // y-m-d
		cout<<Year(s[0],s[1])<<'-'<<s[3]<<s[4]<<'-'<<s[6]<<s[7]<<endl ;
	}
	if(FUCK(s[0],s[1],s[3],s[4],s[6],s[7]) && !(isSame(s[0],s[1],s[3],s[4]) && isSame(s[0],s[1],s[6],s[7]))){ // m-d-y
		cout<<Year(s[6],s[7])<<'-'<<s[0]<<s[1]<<'-'<<s[3]<<s[4]<<endl ;
	}
	if(FUCK(s[3],s[4],s[0],s[1],s[6],s[7]) && !(isSame(s[0],s[1],s[3],s[4]) || isSame(s[0],s[1],s[6],s[7]))){ // d-m-y
		cout<<Year(s[6],s[7])<<'-'<<s[3]<<s[4]<<'-'<<s[0]<<s[1]<<endl ;
	}
	
	return 0;
}

都做到这了,说是要以年月日顺序输出。我的建议是吃粑粑去吧。

#include<bits/stdc++.h>
using namespace std;    
int s[12]={31 ,28,31,30,31,30,  31 ,  31 ,  30 ,  31 ,  30 ,  31 };
int change(string a)
{
    istringstream is(a); //构造输入字符串流,流的内容初始化为“12”的字符串   
    int i;   
    is >> i; //从is流中读入一个int整数存入i中  
    return i;
}
int w1(int year,int mouth,int day)
{
    int a1;
    if(year>=0&&year<=59)
    a1=2000+year;
    if(year>=60&&year<=99)
     a1=1900+year;
    if((a1%4==0&&a1%100!=0)||(a1%400==0))
    s[1]=29;
    if(mouth<=0||mouth>12)return 0;
    if(s[mouth-1]<day||day==0 )return 0;
    return a1*10000+mouth*100+day;
}

int main()
{
    string a;
    cin>>a;
    string m=a.substr(0,2);
    string n=a.substr(3,2);
    string w=a.substr(6,2);
    int year,mouth,day;
    year=change(m);
    mouth=change(n);
    day=change(w);
    long long  r1=w1(year,mouth,day);
    long long  r2=w1(day,year,mouth);//月日年 
    long long  r3=w1(day,mouth,year);//日月年 
    
    long long g[3];
      g[0]=r1;
      g[1]=r2;
      g[2]=r3;
  sort(g,g+3);
  r1=g[0];
  r2=g[1];
  r3=g[2];
//cout<<r1<<" "<<r2<<" "<<r3<<endl;
if(r1!=0)
  {cout<<r1/10000<<"-";
    printf("%02d",(r1/100)%100);
    cout<<"-";
    printf("%02d\n",r1%100);}
if(r2!=0&&r2!=r1)
{    cout<<r2/10000<<"-";
    printf("%02d",(r2/100)%100);
    cout<<"-";
    printf("%02d\n",r2%100);
}
if(r3!=0&&r3!=r2&&r3!=r1)
{
  cout<<r3/10000<<"-";
    printf("%02d",(r3/100)%100);
    cout<<"-";
    printf("%02d\n",r3%100);
}
    
    //cout<<r2/10000<<"-"<<(r2/100)%100<<"-"<<r2%100<<endl;
    //cout<<r3/10000<<"-"<<(r3/100)%100<<"-"<<r3%100<<endl;
    return 0;
}

填空 算式900 next_permutation() 全排列

小明的作业本上有道思考题:
看下面的算式:

(□□□□-□□□□)*□□=900

其中的小方块代表 000 ~ 999 的数字,这 101010 个方块刚好包含了 000 ~ 999 中的所有数字。 注意:000 不能作为某个数字的首位。

小明经过几天的努力,终于做出了答案!如下:
(5012-4987)*36=900
用计算机搜索后,发现还有另外一个解,本题的任务就是:请你算出这另外的一个解。
注意:输出格式需要与示例严格一致; 括号及运算符号不要用中文输入法; 整个算式中不能包含空格。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a[10] = {0,1,2,3,4,5,6,7,8,9};
	do{
		int x = a[0] * 1000 + a[1] * 100 + a[2] * 10 + a[3] ;
		int y = a[4] * 1000 + a[5] * 100 + a[6] * 10 + a[7] ;
		int z = a[8] * 10 + a[9] ;
		int all = (x - y) * z ;
		if(all == 900 && x != 5012 && y != 4987){
			printf("(%d-%d)*%d=900",x,y,z) ;
			return 0 ;
		}
	}while(next_permutation(a,a + 10));
	
	return 0 ;
}

next_permutation就不用自己写dfs来回溯了。但是如果原来的数并非sort过,只会从当前位置开始排列。
在这里插入图片描述

所以如果要全排列得加一个sort,如果是string等,next_permutation(a.begin(),a.end())

包子凑数

小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有 N 种蒸笼,其中第 i 种蒸笼恰好能放 Ai 个包子。每种蒸笼都有非常多笼,可以认为是无限笼。

每当有顾客想买 X 个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有 X 个包子。比如一共有 3 种蒸笼,分别能放 3、4 和 5 个包子。当顾客想买 11 个包子时,大叔就会选 2 笼 3 个的再加 1 笼 5 个的(也可能选出 1 笼 3 个的再加 2 笼 4 个的)。

当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有 3 种蒸笼,分别能放 4、5 和 6 个包子。而顾客想买 7 个包子时,大叔就凑不出来了。

小明想知道一共有多少种数目是包子大叔凑不出来的。

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5;
int dp[maxn], a[105];//dp[i]代表拿i个包子是否可能,1表示可能,0表示不可能 

int gcd(int a, int b) {
    return b?gcd(b,a%b):a;
}
int main() {
    int n, g, num = 0;
    cin>>n;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    g = gcd(a[0], a[1]);
    for (int i = 2; i < n; i++) {
        g = gcd(g, a[i]);
    }//两两求最大公因子
    if (g != 1) cout << "INF" << endl;   //不互质,则有无限种都不能表示 
    else {
        dp[0] = 1;//0个包子肯定可以
        for (int i = 0; i < n; i++) {
            for (int j = 0; j +a[i]< maxn; j++) {
                if (dp[j]) {
                    dp[j + a[i]] = 1;
                }
            }
        }
        for (int i = 0; i < maxn; i++) {
            if(!dp[i]) {
                num++;
            }
        }
        cout << num << endl;
    }
    return 0;
}

先判断所有的数是否存在互质,如果不互质,INF。
然后直接在足够大的范围内查找不能表示的数,由于限制数最大100,那么最大的num当测试用例为 100 99 时能取到,此时分界的数为 98 * 99 < 1e4 ,所以 maxn 在 1e4 以上即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值