第九届蓝桥杯C/C++B组省赛原始代码与题解

#第九届蓝桥杯C/C++ B组



标签(空格分隔): C/C++

---
    转眼间,蓝桥杯省赛已经过去一个月了。这一个月里,我并没有再学算法,就今天开始看了看。于是想总结下这次省赛。
    还记得,刚考试完,着急的对着答案,越对越难受,觉得自己完蛋了。毕竟自己学了一个多月的算法,如果进不了B组的国赛,那我就太垃圾了。忐忑的等了几天,终于出了成绩。出乎意料的还不错,一等奖第十几名。但是感觉自己做的真的不好,错误太多了。在兴奋了一会以后,又开始觉得B组太水了,如果我去了A组,估计就是炮灰了,撑死了二等奖(自我估计)。然后我看到了A组一等奖全是什么清华,北航的,更映证了我的想法。
    考完试以后,我用U盘把我的代码还有题目全拷贝下来了。下面我会放出我考试的时候的所有原始代码。给后来人做个路标,如果你能做的比我好,B组省赛肯定没问题。

第一题 

    标题:第几天


2000年的1月1日,是那一年的第1天。
那么,2000年的5月4日,是那一年的第几天?
注意:需要提交的是一个整数,不要填写任何多余内容。
这个就不多说了,我直接手算的。
31+29+31+30+4=125


第二题



标题:明码


汉字的字形存在于字库中,即便在今天,16点阵的字库也仍然使用广泛。
16点阵的字库把每个汉字看成是16x16个像素信息。并把这些信息记录在字节中。


一个字节可以存储8位信息,用32个字节就可以存一个汉字的字形了。
把每个字节转为2进制表示,1表示墨迹,0表示底色。每行2个字节,
一共16行,布局是:


    第1字节,第2字节
    第3字节,第4字节
    ....
    第31字节, 第32字节


这道题目是给你一段多个汉字组成的信息,每个汉字用32个字节表示,这里给出了字节作为有符号整数的值。


题目的要求隐藏在这些信息中。你的任务是复原这些汉字的字形,从中看出题目的要求,并根据要求填写答案。


这段信息是(一共10个汉字):
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0 
16 64 16 64 34 68 127 126 66 -124 67 4 66 4 66 -124 126 100 66 36 66 4 66 4 66 4 126 4 66 40 0 16 
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0 
0 -128 64 -128 48 -128 17 8 1 -4 2 8 8 80 16 64 32 64 -32 64 32 -96 32 -96 33 16 34 8 36 14 40 4 
4 0 3 0 1 0 0 4 -1 -2 4 0 4 16 7 -8 4 16 4 16 4 16 8 16 8 16 16 16 32 -96 64 64 
16 64 20 72 62 -4 73 32 5 16 1 0 63 -8 1 0 -1 -2 0 64 0 80 63 -8 8 64 4 64 1 64 0 -128 
0 16 63 -8 1 0 1 0 1 0 1 4 -1 -2 1 0 1 0 1 0 1 0 1 0 1 0 1 0 5 0 2 0 
2 0 2 0 7 -16 8 32 24 64 37 -128 2 -128 12 -128 113 -4 2 8 12 16 18 32 33 -64 1 0 14 0 112 0 
1 0 1 0 1 0 9 32 9 16 17 12 17 4 33 16 65 16 1 32 1 64 0 -128 1 0 2 0 12 0 112 0 
0 0 0 0 7 -16 24 24 48 12 56 12 0 56 0 -32 0 -64 0 -128 0 0 0 0 1 -128 3 -64 1 -128 0 0 




注意:需要提交的是一个整数,不要填写任何多余内容。
这个我开始还想拿手算,还好2分钟后我就反应过来了,直接位运算。还有人提到什么补码,我不知道哪用到。


 
 #include <iostream>
#include <cstdio>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
void get(int x){
	int n=0;
	int a[16];
	for(int i=0;i<16;i++){
		
		n=(x&1);
		a[i]=n;
		x=x>>1;
		//printf("%d",n);
	}
	for(int i=15;i>=0;i--){
		printf("%d",a[i]);
	}
} 
int main(int argc, char *argv[]) {
	int k;
	for(int m=0;m<10;m++){
		for(int i=0;i<16;i++){
		for(int j=0;j<2;j++){
			scanf("%d",&k);
			get(k);
		}
		cout<<endl;
	}
	}
	return 0;
}


直接答案就出来了9的9次方。我直接用电脑计算器算的。答案387420489。


第三题



标题:乘积尾零


如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零?


5650 4542 3554 473 946 4114 3871 9073 90 4329 
2758 7949 6113 5659 5245 7432 3051 4434 6704 3594 
9937 1173 6866 3397 4759 7557 3070 2287 1453 9899 
1486 5722 3135 1170 4014 5510 5120 729 2880 9019 
2049 698 4582 4346 4427 646 9742 7340 1230 7683 
5693 7015 6887 7381 4172 4341 2909 2027 7355 5649 
6701 6645 1671 5978 2704 9926 295 3125 3878 6785 
2066 4247 4800 1578 6652 4616 1113 6205 3264 2915 
3966 5291 2904 1285 2193 1428 2265 8730 9436 7074 
689 5510 8243 6114 337 4096 8199 7313 3685 211 


注意:需要提交的是一个整数,表示末尾零的个数。不要填写任何多余内容。


这题是我最痛苦的题,我记得考试前一天,我还在模拟赛上看到了相似的题,但是我并没有做。觉得肯定不考。然后自己找规律,好像和5的个数有关。然后就GG了,做完以后,我觉得不对,但是我很着急,因为怕时间不够,就特别赶。当然,最主要的原因是,我没有看到分值分配的那个word,所以就没管。如果看到了,我会回来做的,这题是我失误很大的一道题。


   
 #include <iostream>
#include <cstdio>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */


int main(int argc, char *argv[]) {
	int n;
	int cnt=0;
	for(int i=0;i<100;i++){
		scanf("%d",&n);
		if(n%5==0){
			cnt++;
		}
	}
	cout<<cnt<<endl;
	return 0;
}




考完以后贼痛苦,因为感觉很简单。直接用longlong每个数相乘,再余一下就ok,遇到10就+1。
#include <iostream>
#include <stdio.h>
using namespace std;
const int mod=100000000;
int main()
{   long long s=1,n;
    int cnt=0;


    while(scanf("%I64d",&n)!=EOF){
        s=n*s%mod;
        while(s%10==0){
            s=s/10;
            cnt++;
            printf("cnt=%d,s=%I64d\n",cnt,s);
        }
        cout<<cnt<<endl;
    }


    return 0;
}

答案31。

第四题



标题:测试次数


x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。


x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。


如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。
特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。
如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n


为了减少测试次数,从每个厂家抽样3部手机参加测试。


某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?


请填写这个最多测试次数。


注意:需要填写的是一个整数,不要填写任何多余内容。


这题,很蛋疼,我直接就想着是二分法。还在纳闷,这题为什么给三个手机,是不是出错了。考完以后,突然反应过来了。
原始代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */


int main(int argc, char *argv[]) {
	int cnt[10000];
	for(int g=1;g<=1000;g++){
		int l=1,r=1000;
		while(r-l>1){
			int mid=(l+r)/2;
			if(mid<=g){
				l=mid;
				cnt[g]++;
				printf("g=%d,mid=%d,cnt=%d\n",g,mid,cnt[g]);
			}
			else r=mid;
		}
	}
	sort(cnt+1,cnt+1001);
	cout<<cnt[1000]<<endl;
	return 0;
}


我还sb的把每层楼都用2分做了一边。最后不确定,直接用手算了。
写的10。
后来,才发现因为有三个手机,所以得用动态规划来做。这题我没做出来,我也不可惜。因为我就算发现了,我当时也是不可能做出来的。对动态规划,我也只是简单的理解了,会做背包题。
修改后:


#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int dp[10][1002];
int n=1000;
int main()
{
    int i,j,k;
    for(i=0;i<10;i++){
        fill(dp[i]+1,dp[i]+1001,100000);
    }
    dp[1][1]=1;
    //dp[1][999]=999;
    for(i=1;i<5;i++){
        for(j=1;j<1001;j++){
            for(k=1;k<=j;k++){
                if(i==1&&j==1)continue;
                dp[i][j]=min(dp[i][j],max(dp[i-1][k-1]+1,dp[i][j-k]+1));
            }
        }
    }
    cout<<dp[3][1000]<<endl;
    return 0;
}


dp[i][j]:i个球,j层楼,需要的最小次数。
递推关系是,dp[i][j]=min(dp[i][j],max(dp[i-1][k-1]+1,dp[i][j-k]+1));  1=<k<=j;
理解就是,如果碎了,那么说明得向下测,这时次数+1,手机数i-1,需要测的层数k-1。如果没碎,测试次数+1,楼层变为j-k,也就是k以上的楼层。k从1到j,取最小的次数,就是最优的方法。
最后答案时19。


第五题



标题:快速排序。 


以下代码可以从数组a[]中找出第k小的元素。




它使用了类似快速排序中的分治算法,期望时间复杂度是O(N)的。




请仔细阅读分析源码,填写划线部分缺失的内容。

#include <stdio.h>


int quick_select(int a[], int l, int r, int k) {
	int p = rand() % (r - l + 1) + l;
	int x = a[p];
	{int t = a[p]; a[p] = a[r]; a[r] = t;}
	int i = l, j = r;
	while(i < j) {
		while(i < j && a[i] < x) i++;
		if(i < j) {
			a[j] = a[i];
			j--;
		}
		while(i < j && a[j] > x) j--;
		if(i < j) {
			a[i] = a[j];
			i++;
		}
	}
	a[i] = x;
	p = i;
	if(i - l + 1 == k) return a[i];
	if(i - l + 1 < k) return quick_select( _____________________________ ); //填空
	else return quick_select(a, l, i - 1, k);
}
	
int main()
{
	int a[] = {1, 4, 2, 8, 5, 7, 23, 58, 16, 27, 55, 13, 26, 24, 12};
	printf("%d\n", quick_select(a, 0, 14, 5));
	return 0;
}



注意:只填写划线部分缺少的代码,不要抄写已经存在的代码或符号。


这题是让我第二难受的,因为太考验阅读理解了。第k小,那最小的是第0小还是第1小呢?因为那个数组下标就是从0开始了,让我误以为第0小是最小的。所以我就sb了。虽然说,可以通过将每个k遍历输出来确定它的说法,但是当时那么紧张,哪想的出来,本来说回头再想的,但是当我试了几次,试出了8时(实际上第五小是7),我就坚定了自己的从0开始的想法,于是就GG了。
我填的是,a,l,i+1,k。
这是种快排的方法,虽然说,他要求了时间复杂度O(n),但是只要是试对了,估计就有不少分。贼惨。
不过到现在我也没有看这种快排的方法,但我知道这是种改进的快排。
正确的答案可以自己去搜一下。


第六题



标题:递增三元组


给定三个整数数组
A = [A1, A2, ... AN], 
B = [B1, B2, ... BN], 
C = [C1, C2, ... CN],
请你统计有多少个三元组(i, j, k) 满足:
1. 1 <= i, j, k <= N  
2. Ai < Bj < Ck  


【输入格式】 
第一行包含一个整数N。
第二行包含N个整数A1, A2, ... AN。
第三行包含N个整数B1, B2, ... BN。
第四行包含N个整数C1, C2, ... CN。


对于30%的数据,1 <= N <= 100  
对于60%的数据,1 <= N <= 1000 
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000 


【输出格式】
一个整数表示答案


【样例输入】
3
1 1 1
2 2 2
3 3 3


【样例输出】
27 




资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms




请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。


注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。


提交程序时,注意选择所期望的语言类型和编译器类型。


做到这,我惊讶了,居然今年有五道编程题。这题很容易就想到,先排个序。然后遍历两遍,分别找出,比每个B大的C的个数,和比B小的A的个数,乘一下就好了。于是我就写出了非常丑陋的二重循环,由于我当时以为lower_bound()函数只能查找相同的某个值的位置。然后二分搜索本来我可以自己写的,第四题没必要写的我都写了,但是当时就太赶时间了,我真的菜。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int a[100005],b[100005],c[100005];
int main(int argc, char *argv[]) {
	int n,j,i;
	int d[100005];
	int dp[100005];
	scanf("%d",&n);
	for(j=1;j<=n;j++){
		scanf("%d",&a[j]);
	}
	for(j=1;j<=n;j++){
		scanf("%d",&b[j]);
	}
	for(j=1;j<=n;j++){
		scanf("%d",&c[j]);
	}
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	sort(c+1,c+n+1);
	int cnt;
	long long sum=0;
	for(i=1;i<=n;i++){
		cnt=0;
		for(j=1;j<=n;j++){
			if(b[i]>=c[j])continue;
			cnt=n-j+1;
			break;
		}
		d[i]=cnt;
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			if(a[i]>=b[j])continue;
			dp[i]+=d[j];
		}
		sum+=dp[i];
	}
	printf("%I64d\n",sum);
	return 0;
}


这样的话只能过60%的数据左右。
改进后:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int a[100005],b[100005],c[100005];
bool judge(int i,int mid){
    if(b[i]>=c[mid])return true;
    else return false;
}
bool judge2(int i,int mid){
    if(b[i]>a[mid])return true;
    else return false;
}
int main(int argc, char *argv[]) {
	int n,j,i;
	int d[100005];
	int dp[100005];
	scanf("%d",&n);
	for(j=1;j<=n;j++){
		scanf("%d",&a[j]);
	}
	for(j=1;j<=n;j++){
		scanf("%d",&b[j]);
	}
	for(j=1;j<=n;j++){
		scanf("%d",&c[j]);
	}
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	sort(c+1,c+n+1);
	int cnt;
	long long sum=0;
	for(i=1;i<=n;i++){
		cnt=0;
		int l=1,r=n;
		while(r-l>1){
            int mid=(l+r)>>1;
            if(judge(i,mid))l=mid;
            else r=mid;
		}
		if(b[i]>=c[l])l=r;
		printf("%d\n",l);
		cnt=n-l+1;
		d[i]=cnt;
		printf("d[i]=%d\n",d[i]);
//		for(j=1;j<=n;j++){
//			if(b[i]>=c[j])continue;
//			cnt=n-j+1;
//			break;
//		}
//		d[i]=cnt;
	}
	for(i=1;i<=n;i++){
		int l=1,r=n;
		while(r-l>1){
            int mid=(l+r)>>1;
            if(judge2(i,mid))l=mid;
            else r=mid;
		}
		if(b[i]>a[r]) l=r;
		printf("l=%d\n",l);
		sum+=l*d[i];
	}
	printf("%I64d\n",sum);
	return 0;
}


这样的话,nlogn,应该可以得满分。

第七题



标题:螺旋折线


如图p1.png所示的螺旋折线经过平面上所有整点恰好一次。  
对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。  


例如dis(0, 1)=3, dis(-2, -1)=9  


给出整点坐标(X, Y),你能计算出dis(X, Y)吗?


【输入格式】
X和Y  


对于40%的数据,-1000 <= X, Y <= 1000  
对于70%的数据,-100000 <= X, Y <= 100000  
对于100%的数据, -1000000000 <= X, Y <= 1000000000  


【输出格式】
输出dis(X, Y)  




【样例输入】
0 1


【样例输出】
3




资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms




请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。


注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。


提交程序时,注意选择所期望的语言类型和编译器类型。


这题我就是找啊找啊找规律,浪费了很久的时间,最终还犯了sb。
我不讲为啥了,因为我也不记得了。就是找的规律,而且不是很好。我看有的人找的规律,不怎么耗时间。
#include <iostream>
#include <cstdio>
#include <stdlib.h>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
typedef long long ll;


int main(int argc, char *argv[]) {
	//ll dp[1000000000];
	ll x,y;
	scanf("%I64d %I64d",&x,&y);
	ll n=max(abs(x),abs(y));
	ll ans;
	if(x<=0&&y>=0){
		ans=n+x+y+n-1;
	}
	else if(x>=0&&y>=0){
		ans=n*2+n-y+x+n-1;
	}
	else if(x>=0&&y<=0){
		ans=n*4+n-x-y+n-1;
	}
	else if(x<=0&&y<=0&&x<=y-1){ 
		ans=n+y-1;
	}
	else{
		ans=n*6+n+y-x+n-1;
	}
	ll num=1;
	ll res=1;
	int s=0;//此处!!!
	while(num<n){
		s+=num;
		num++;
	}
	res+=s*8;
	res+=ans;
	cout<<res<<endl;
	//printf("%I64d\n",res);
	return 0;
}


本来一切还好,我的复杂度也不是很高,应该能得不少分,也就是极限附近的数可能超时了。因为后来我运行的时候,耗时感觉不少。但是我后来发现,我的答案,居然在int型就溢出了,但是我明明开的是long long啊。最后检查发现,我那个s是int型的,导致溢出了,我特么简直是个智障,所有的都开ll了,结果就漏了一个。导致我又少了几分。
到这,大家应该可以感觉到,我做的其实并不是很完美,所以我觉得好水。


第八题



标题:日志统计


小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。其中每一行的格式是:


ts id  


表示在ts时刻编号id的帖子收到一个"赞"。  


现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。  


具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。  


给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。  


【输入格式】
第一行包含三个整数N、D和K。  
以下N行每行一条日志,包含两个整数ts和id。  


对于50%的数据,1 <= K <= N <= 1000  
对于100%的数据,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000  


【输出格式】
按从小到大的顺序输出热帖id。每个id一行。  


【输入样例】
7 10 2  
0 1  
0 10    
10 10  
10 1  
9 1
100 3  
100 3  


【输出样例】
1  
3  




资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms




请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。


注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。


提交程序时,注意选择所期望的语言类型和编译器类型。


这题我做的也不是很好,但是我并不太怨,因为我当时并没有用过尺取法,所以用了双重循环也可以理解。估计得了50%的分吧。
这题也就是硬写,本来想定义一个结构体的,结果当时重载符号我不记得怎么写了,因为我没用过。所以还是用了pair,然后按时间排个序,就强找了。
好吧,在我写这段话,回头看代码的时候,我又一次发现了自己的垃圾。大家请看:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string.h>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
typedef pair<int,int> P;
P p[100002];
//set<int> st;
int n,d,k;
vector<int> G;
int main(int argc, char *argv[]) {
	int id[100002];
	scanf("%d%d%d",&n,&d,&k);
	for(int i=0;i<n;i++){
		scanf("%d%d",&p[i].first,&p[i].second);
	}
	sort(p,p+n);
	for(int k=0;k<n;k++){
		if(p[k].first==p[k-1].first&&k!=0)continue;
		for(int i=k;i<n;i++){
			if(p[i].first>=p[k].first&&p[i].first<=p[k].first+d-1){
				id[p[i].second]++;
				//printf("k=%d,i=%d,p.second=%d\n",k,i,p[i].second);
			}
			//break;
		} 
		for(int j=0;j<n;j++){
			//printf("id=%d\n",id[j]);
			if(id[j]>=2){
				id[j]=1000000;
			}
		}
	}
	for(int i=0;i<n;i++){
		if(id[i]>=1000000)printf("%d\n",i);
	}
	
	return 0;
}


不看也没关系。我就说为啥吧,题目输入,n,d,k,然后k代表的是不少于k个赞。然后我不知道为啥,就用k从0开始作循环,然后找大于2个赞的帖子。emmm...特么的例子当然能过。这是我刚刚发现的,再一次说明了,我做的真的不太好。然后这题估计就是0分了。mmp,花了我不少时间呢。
后来我用尺取法做了:
#include <iostream>
#include <stdio.h>
#include <map>
#include <algorithm>
using namespace std;
pair<int,int> ts[100002];
//int ts[100002],id[100002];
int main()
{
    int n,d,k;
    scanf("%d%d%d",&n,&d,&k);
    for(int i=0;i<n;i++){
        scanf("%d%d",&ts[i].first,&ts[i].second);
    }
    sort(ts,ts+n);
    map<int,int> count;
    int s=0,t=0;
    int res[100002];
    int m=0;
    for(;;){
        while(t<n&&ts[t].first-ts[s].first<=9){
            if(++count[ts[t++].second]>=k)
                res[m++]=ts[t-1].second;
            }


        if(t==n){
            break;
        }
        count[ts[s++].second]--;


    }
    sort(res,res+m);
    int mm=unique(res,res+m)-res;
    for(int i=0;i<mm;i++){
        printf("%d\n",res[i]);
    }
    return 0;
 }



简单又快捷,还能拿满分的。
尺取法的思想就是不断的推进区间,然后找最佳答案。当然解释尺取法不是一句话两句话的事,建议百度。
这题是很适合尺取法的,因为要找时间的区间。

第九题



标题:全球变暖


你有一张某海域NxN像素的照片,"."表示海洋、"#"表示陆地,如下所示:


.......
.##....
.##....
....##.
..####.
...###.
.......


其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。  


由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。  


例如上图中的海域未来会变成如下样子:


.......
.......
.......
.......
....#..
.......
.......


请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。  


【输入格式】
第一行包含一个整数N。  (1 <= N <= 1000)  
以下N行N列代表一张海域照片。  


照片保证第1行、第1列、第N行、第N列的像素都是海洋。  


【输出格式】
一个整数表示答案。


【输入样例】

.......
.##....
.##....
....##.
..####.
...###.
.......  


【输出样例】
1  






资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms




请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。


注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。


提交程序时,注意选择所期望的语言类型和编译器类型。


这题,是我做的最好的一道题了,也是我能拿十几名的最大功臣。看到这题,我流下了激动的泪水。因为往年的题,很多都用了dfs,今年做了那么多道都没有。终于看到了,dfs大法,直接干掉了这题。用白小纯的话来说就是,我dfs弹指间,第九题灰飞烟灭。
我看了下数据范围,确认不会超时,就放心的用了dfs,这个dfs比普通的要难一点,要用到2次。
#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
char s[1002][1002];
int used[1002][1002];
int n;
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int cnt=0,cnt2=0;
bool if_dest(int x,int y){
	int nx,ny;
	bool flag=false;
	for(int i=0;i<4;i++){
		nx=x+dx[i];
		ny=y+dy[i];
		if(s[nx][ny]=='.'&&used[nx][ny]==0){
			flag=true;
			break;
		}
	}
	return flag;
}
void dfs(int x,int y){
	int nx,ny;
	//printf("dfs(%d,%d)\n",x,y);
	for(int i=0;i<4;i++){
		nx=x+dx[i];
		ny=y+dy[i];
		if(s[nx][ny]=='#'&&used[nx][ny]==0){
			used[nx][ny]=1;
			dfs(nx,ny);
		}
	}
}
int main(int argc, char *argv[]) {
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",&s[i]);
	}	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(s[i][j]=='#'&&!used[i][j]){
				used[i][j]=1;
				dfs(i,j);
				cnt++;
			}
		}
	}
	memset(used,0,sizeof(used));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(s[i][j]=='#'&&if_dest(i,j)){
				used[i][j]=1;
				s[i][j]='.';
			}
		}
	}
	memset(used,0,sizeof(used));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(s[i][j]=='#'&&!used[i][j]){
				used[i][j]=1;
				dfs(i,j);
				cnt2++;
			}
		}
	}
	int r=cnt-cnt2;
	printf("%d\n",r);
	return 0;
}



思想就是先dfs判断一下,又几个岛屿,记为cnt。然后淹没一下。再用dfs找下几座岛,记作cnt2。
然后答案就是cnt-cnt2。
有人说,有可能淹完了,反而岛还多了。这时答案应该是0。
还好我没有想到这种情况,因为我知道的话又得纠结了。从1到2,为毛是0,不是-1呢。这是有争议的。
不过我认为,蓝桥杯的数据可能不包含这种情况。


第十题



标题:乘积最大


给定N个整数A1, A2, ... AN。请你从中选出K个数,使其乘积最大。  


请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以1000000009的余数。  


注意,如果X<0, 我们定义X除以1000000009的余数是负(-X)除以1000000009的余数。
即:0-((0-x) % 1000000009)


【输入格式】
第一行包含两个整数N和K。  
以下N行每行一个整数Ai。  


对于40%的数据,1 <= K <= N <= 100  
对于60%的数据,1 <= K <= 1000  
对于100%的数据,1 <= K <= N <= 100000  -100000 <= Ai <= 100000  


【输出格式】
一个整数,表示答案。




【输入样例】
5 3 
-100000   
-10000   
2   
100000  
10000  


【输出样例】
999100009


再例如:
【输入样例】
5 3 
-100000   
-100000   
-2   
-100000  
-100000


【输出样例】
-999999829




资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 1000ms




请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。


注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。


提交程序时,注意选择所期望的语言类型和编译器类型。


这道题我是拒绝的,并且我也没有当时提交的代码。因为后来我又修改了半天,结果写不出来。这道题还用了我40分钟的时间呢,结果毛都没有。当时就匆匆交了个过了一个例子的代码。
现在我也不想看,感觉当时没读懂题。果然是阅读理解杯。我就把最后我的代码贴出来吧。肯定不对。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stdlib.h>
using namespace std;
int n,k;


/* run this program using the console pauser or add your own getch, system("pause") or input loop */
typedef long long ll;
int a[100002]; 
bool cmp(const ll a,const ll b){
	return a>b;
}
void permutation(){
	int i=0;
	ll mul[100002];
	fill(mul,mul+100002,1);
	do{
		for(int j=0;j<k;j++){
			mul[i]=(mul[i]*a[j]%1000000009)%1000000009;
		}
		i++;
	}while(next_permutation(a,a+n));
	sort(mul,mul+1000002,cmp);
	printf("%I64d\n",mul[0]);
	
}
int main(int argc, char *argv[]) {


	
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
		//if(a[i]<0)d[i]=1;
		//a[i]=abs(a[i]);
	}
	sort(a,a+n);
//	for(int i=0;i<n;i++){
//		printf("%d ",a[i]);
//	}
//	ll mul[100002];
//	fill(mul,mul+100002,1);
//	int sum=0;
//	for(int i=0;i<=n-k;i--){
//		for(int j=i;j<i+k;j++){
//			mul[i]=(mul[i]*a[j]%1000000009)%1000000009;
//		}
//	}
	permutation();
//	sort(mul,mul+n-k+1,)
//	cout<<mul<<endl;


	return 0;
}


是真的蛋疼。


吐槽&总结&感想

阅读理解杯是真的蛋疼。还有就是为什么要必须用dev cpp啊,为什么不能用codeblock啊,这个不能自动美化还有不提示关键字真的要了亲命了。还有关于今年的难度,个人感觉是比去年还要难的,因为往年比较暴力,今年就有许多技巧。但是实际上,个人认为,今年的题,难度对没学过算法,不会dfs的人来说简单了点。因为今年的题区分度不是很高,摔手机的大家都不会,最后一题也是。就是难的难,简单的简单。然后普通的题大数据规模的不好过,但是小规模的大家都能过。所以区分度就低了。

大家也能看出,其实我是有非常多的失误的。所以B组省一不是什么高不可攀的,反而是很容易达到的。当然我做的这么差,还那么靠前,所以我对国赛就有了信心。希望能水个国一国二出来。从今天开始,继续学习算法,当然主要时间还是得用在高数线代英语上。

反正就是既不要妄自尊大,觉得自己很NB,也不要妄自菲薄,觉得自己无药可救。
保持饥饿吧...
我的遗憾就是太晚发现自己不喜欢电子系,然后没有机会再参加ACM了,毕竟都大三了,还要准备跨考考研。如果我打过ACM,我自信可以混出个名堂的。
但是也没有什么可以后悔的,因为如果当时去了计科,我可能并不会打ACM。因为我大一大二那是沉迷在LOL中的。
学习使我痛苦,但是为了以后少点后悔,那么现在就得努力。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值