CSP2013年,2014年,2015年刷题、2016年——2023年2月23日

前两题就比较简单,不需要很难的算法就能解决,所以不写题解和思路了。
在这里插入图片描述

细节问题

输出的时候, 数字与数字之间只能空一格还是两个, 最好把测试用例复制一下。 不要自己随便加。不然不知道哪里出了问题。
在这里插入图片描述
在这里插入图片描述

2013年 12月 第一题 出现次数最多的数字

就是一个桶排序的思路,数组开到10000 不是 1000(注意)
在这里插入图片描述

2013年 12月 第二题 IBSN 号码

简单模拟一下过程。
正常乘法即可。
在横轴上放了n个相邻的矩形,每个矩形的宽度是1,而第i(1 ≤ i ≤ n)个矩形的高度是hi。这n个矩形构成了一个直方图。例如,下图中六个矩形的高度就分别是3, 1, 6 , 5 , 2 , 3。
在这里插入图片描述

#include<iostream>
using namespace std;
int main()
{
    char s[14];
    cin >> s;
    int sum = (s[0] - '0');
    int j = 2;
    for (int i = 2; i <= 4; i++){
        sum += (s[i]-'0') * i;
    }
    for (int i = 6; i <= 10; i++){
        sum += (s[i] - '0') * (i-1);
    }
    if (sum % 11 == (s[12] - '0')&& (s[12]!='X'))
    {
        cout << "Right" << endl;
    }
    else if (s[12] == 'X'&& sum % 11 == 10)
    {
        cout << "Right" << endl;
    }
    else{
        for (int i = 0; i <= 11; i++){
            cout << s[i];
        }
        if (sum % 11 == 10)
        {
            cout << 'X';
        }
        else
        {
            cout << sum % 11;
        }
    }
    return 0;
}

2013年 12月 第三题 最大矩形

在横轴上放了 n个相邻的矩形,每个矩形的宽度是 1,而第 i(1≤i≤n)个矩形的高度是 hi。这 n个矩形构成了一个直方图。
例如,下图中六个矩形的高度就分别是 3,1,6,5,2,3。
在这里插入图片描述
在这里插入图片描述

枚举:

下边界不用确定。 只需要确定上边界,左右边界即可。
上边界确定用n次。
枚举到上边界之后,要确定左右边界。
左边界很明显是距离本个数字最远的小于本数字的距离。
右边界是距离本个数字最远的小于本数字的距离(如果到头就停止。)
按照这样的思路进行枚举,则发现时间复杂度是 O(n^2)
1000的数据量可以通过。
在这里插入图片描述

在这里插入图片描述
第二次做对。
遍历的位置没有找对。
从当前的顶的位置,向左向右找。

遍历的方向要注意

简化版本:
可以设置一个全局变量,减少初始化操作。

#include <iostream>
using namespace std;
const int N = 1010;
int h[N];
int n;
int main()
{

    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> h[i];
    int res = 0;//最大面积
    for(int i = 1; i <= n; i++)
    {
        int l, r;
        for(int j = i; j >= 1; j--)//左侧边界
        {
            if(h[i] > h[j]) break;
            l = j;
        }

        for(int j = i; j <= n; j++)//右侧边界
        {
            if(h[i] > h[j]) break;
            r = j;
        }
        res = max(res, h[i] * (r - l + 1));
    }
    cout << res;
}

我写的繁琐版本:

#include<iostream>
using namespace std;

int main()
{
    int num = 0;
    cin >> num;
    int a[1010];
    int area[1010];
    for (int i = 0; i<1010 ; i++)
    {
        area[i] = 0;
        if (i < num)
        {
            cin>>a[i];
        }
        else
        {
            a[i] = 0;
        }
    }
    int L = 0, R = 0, H = 0, D = 1;

    for (int i = 0; i<num; i++)
    {
        H = a[i];   //   每一个顶都试一遍。选最大的。
        for (int j = i; j >=0; j--)
        {
            if (a[j] < a[i])  break;
                L = j;
        } 
        for (int j = i; j <=num; j++)
        {
            if (a[j] < a[i])  break;
            R = j;
        }
        area[i] = H * (R-L+1);
    }   
    int max = area[0],k=0;
    for (int i = 0; i < num; i++)
    {
        if (area[i] > max)
        {
            max = area[i];
            k = i;
        }
    }
    cout << area[k];
    return 0;
}

2013年 12月 第四题 有趣的数 ACWing 3195

我们把一个数称为有趣的,当且仅当:
它的数字只包含 0,1,2,3,且这四个数字都出现过至少一次。
所有的 0 都出现在所有的 1 之前,而所有的 2 都出现在所有的 3之前。最高位数字不为 0。
因此,符合我们定义的最小的有趣的数是 2013。
除此以外,4 位的有趣的数还有两个:2031 和 2301。
请计算恰好有 n 位的有趣的数的个数。
由于答案可能非常大,只需要输出答案除以 109+7的余数。

输入格式

4

输出格式

3

分析

第一要求: 0,1,2,3 至少出现一次
2、0在1 之前。 2在3之前
3 、 首位不为0
这个就是排列组合问题
先分类。
在这里插入图片描述
在这里插入图片描述

求组合数, 用数组 , 加上上面组合数的公式,即可完成计算。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 1010, MOD =1e9+7;
int n;
int C[N][N];

int main()
{
    cin >> n;
    for (int i = 0; i <n ; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            if (!j)C[i][j] == 1;
            else C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;  // 求组合数的公式
        }
    }
    int res = 0;
    for (int k = 2; k <= n - 2; k++)
    {
        res = (res + (LL)C[n - 1][k] * (k - 1) * (n - k - 1)) % MOD;
    }
   
    cout << res;
    return 0;
}

2014年 3月 第2题 窗口

卡了好久
问题1: 边界没考虑
问题2: 编号从1到N, 不是从 0到N-1
在这里插入图片描述

问题描述

在某图形操作系统中,有 N 个窗口,每个窗口都是一个两边与坐标轴分别平行的矩形区域。窗口的边界上的点也属于该窗口。窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的内容。
  当你点击屏幕上一个点的时候,你就选择了处于被点击位置的最顶层窗口,并且这个窗口就会被移到所有窗口的最顶层,而剩余的窗口的层次顺序不变。如果你点击的位置不属于任何窗口,则系统会忽略你这次点击。
  现在我们希望你写一个程序模拟点击窗口的过程。

输入格式

输入的第一行有两个正整数,即 N 和 M。(1 ≤ N ≤ 10,1 ≤ M ≤ 10)
  接下来 N 行按照从最下层到最顶层的顺序给出 N 个窗口的位置。 每行包含四个非负整数 x1, y1, x2, y2,表示该窗口的一对顶点坐标分别为 (x1, y1) 和 (x2, y2)。保证 x1 < x2,y1 2。
  接下来 M 行每行包含两个非负整数 x, y,表示一次鼠标点击的坐标。
  题目中涉及到的所有点和矩形的顶点的 x, y 坐标分别不超过 2559 和  1439。

输出格式

输出包括 M 行,每一行表示一次鼠标点击的结果。如果该次鼠标点击选择了一个窗口,则输出这个窗口的编号(窗口按照输入中的顺序从 1 编号到 N);如果没有,则输出"IGNORED"(不含双引号)。
样例输入
3 4
0 0 4 4
1 1 5 5
2 2 6 6
1 1
0 0
4 4
0 5
样例输出
2
1
1
IGNORED
样例说明
  第一次点击的位置同时属于第 1 和第 2 个窗口,但是由于第 2 个窗口在上面,它被选择并且被置于顶层。
  第二次点击的位置只属于第 1 个窗口,因此该次点击选择了此窗口并将其置于顶层。现在的三个窗口的层次关系与初始状态恰好相反了。
  第三次点击的位置同时属于三个窗口的范围,但是由于现在第 1 个窗口处于顶层,它被选择。
  最后点击的 (0, 5) 不属于任何窗口。

#include<iostream>
using namespace std;
int a[2600][1440];
int D[11][4];
int N,M;
int main(){
    for (int i = 0; i < 2600; i++)
    {
        for (int j = 0; j < 1440; j++)
        {
            a[i][j] = -1;
        }
    }
    cin >> N >> M ;
    for (int i = 0 ; i < N ; i++)
    {
        cin >> D[i][0] >> D[i][1] >> D[i][2] >> D[i][3]  ;       //x1,y1,x2, y2
    }
    for (int i = 0; i < N; i++)
    {
        for (int j = D[i][0]; j <= D[i][2]; j++)
        {
            for (int k = D[i][1]; k <= D[i][3]; k++)
            {
                a[j][k] = i;
            }
       }
    }
    int l = 0, r = 0;
    for (int i = 0; i < M; i++)
    {
        cin >> l >> r;
        if (a[l][r] != -1)
        {
            cout << a[l][r] + 1 <<endl;
            for (int j = D[a[l][r] ][0]; j <= D[a[l][r] ][2]; j++)
            {
                for (int k = D[a[l][r]][1]; k <= D[a[l][r]][3]; k++)
                {
                    a[j][k] = a[l][r];
                }
            }
        }
        else
        {
            cout << "IGNORED"<<endl;
        }
    }
    return 0;
}

2014年 3月 第三题 : 命令行窗口

问题描述

请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项。每个命令行由若干个字符串组成,它们之间恰好由一个空格分隔。这些字符串中的第一个为该命令行工具的名字,由小写字母组成,你的程序不用对它进行处理。在工具名字之后可能会包含若干选项,然后可能会包含一 些不是选项的参数。
  选项有两类:带参数的选项和不带参数的选项。一个合法的无参数选项的形式是一个减号后面跟单个小写字母,如 “-a” 或 “-b” 。而带参数选项则由两个由空格分隔的字符串构成,前者的格式要求与无参数选项相同,后者则是该选项的参数,是由小写字母,数字和减号组成的非空字符串。
  该命令行工具的作者提供给你一个格式字符串以指定他的命令行工具需要接受哪些选项。这个字符串由若干小写字母和冒号组成,其中的每个小写字母表示一个该程序接受的选项。如果该小写字母后面紧跟了一个冒号,它就表示一个带参数的选项,否则则为不带参数的选项。例如, “abⓂ️” 表示该程序接受三种选项,即"-a"(不带参数),“-b”(带参数), 以及"-m"(带参数)。
  命令行工具的作者准备了若干条命令行用以测试你的程序。对于每个命令行,你的工具应当一直向后分析。当你的工具遇到某个字符串既不是合法的选项,又不是某个合法选项的参数时,分析就停止。命令行剩余的未分析部分不构成该命令的选项,因此你的程序应当忽略它们。

输入格式

输入的第一行是一个格式字符串,它至少包含一个字符,且长度不超过 52。格式字符串只包含小写字母和冒号,保证每个小写字母至多出现一次,不会有两个相邻的冒号,也不会以冒号开头。
  输入的第二行是一个正整数 N(1 ≤ N ≤ 20),表示你需要处理的命令行的个数。
  接下来有 N 行,每行是一个待处理的命令行,它包括不超过 256 个字符。该命令行一定是若干个由单个空格分隔的字符串构成,每个字符串里只包含小写字母,数字和减号。

输出格式

输出有 N 行。其中第 i 行以"Case i:" 开始,然后应当有恰好一个空格,然后应当按照字母升序输出该命令行中用到的所有选项的名称,对于带参数的选项,在输出它的名称之后还要输出它的参数。如果一个选项在命令行中出现了多次,只输出一次。如果一个带参数的选项在命令行中出 现了多次,只输出最后一次出现时所带的参数。

样例输入
			albw:x
			4
			ls -a -l -a documents -b
			ls
			ls -w 10 -x -w 15
			ls -a -b -c -d -e -l
样例输出
				Case 1: -a -l
				Case 2:
				Case 3: -w 15 -x
				Case 4: -a -b

遇到的问题: 1 . 输入 不了一行, 注意用 getline(), stringstream

stringstream 中存放多个字符串,实现多个字符串拼接的目的(其实完全可以使用 string 类实现),同时,介绍 stringstream 类的清空方法。

 // 将多个字符串放入 sstream 中
    sstream << "first" << " " << "string,";
    sstream << " second string";
    cout << "strResult is: " << sstream.str() << endl;

在这里插入图片描述
sstream 对输入的字符串进行保存

可变长二维数组:
**vector ops;
一行一行的处理
** 数组的直接存取是最快的 **
所以对于参数和非参数的区分 可以用数组来区分, 然后数组下标,一找就对。

大模拟 思路不清晰, 容易 出错, 卡不出来, 还是要多练习

在这里插入图片描述

#include<iostream>
#include <algorithm>
#include <sstream>
#include <cstring>
#include<vector>
using namespace std;
const int N=30;
bool s1[N],s2[N];   //s1表示非参数, s2 表示有参数的 
string ans[N];    //字母后放的参数 
int n;
 
int main()
{
	string str;
	cin>>str; //输入第一行
	for(int i=0; i< str.size(); i++)
	{
		if(str[i+1] == ':' &&i +1 <str.size())
		{
			s2[str[i]-'a']=true;
			i++;  //这里要跳一格 
		}
		else{
			s1[str[i] - 'a']=true;
		}
	}
	cin>>n;
	getchar();  // 除去后面的换行  注意
	for(int C=1; C<=n; C++)
	{
		cout<<"Case "<<C<<":" ;
		getline(cin, str);   //输入一行 
		stringstream ssin(str);
		vector<string> ops;
		
		while(ssin>>str) ops.push_back(str);
		
		for(int i=0; i<30; i++) 
		{
			ans[i].clear();
		}
		for(int i=1; i<ops.size(); i++)
		{
			if(ops[i][0] != '-' || ops[i][1] <'a'|| ops[i][1]>'z'||ops[i].size() != 2)
			    break;
			int k= ops[i][1]-'a';
			// 判断是 参数 ,还是非参数
			if(s1[k]) ans[k]='/';//  无参数
			else if ( s2[k] && i+1 < ops.size() ) 
			{
				ans[k] = ops[i+1];
				i++;   // 参数这里需要跳一格。
			} 
			else break;
		}
		for(int i=0; i<30;i++)
		{
			if(ans[i].size())  // 这里解决了 字典序问题, 一定是从小到大 输出的。 
			{
				cout<<" -"<< char(i+'a');
				if(s2[i]) 
				{
					cout<<" "<<ans[i];
				}
			}
		}
			cout<<endl;
    }
	return 0;
}

2014年 9月 第一题 相邻数对

问题描述

给定n个不同的整数,问这些数中有多少对整数,它们的值正好相差1。

输入格式

输入的第一行包含一个整数n,表示给定整数的个数。
  第二行包含所给定的n个整数。

输出格式

输出一个整数,表示值正好相差1的数对的个数。

样例输入

6
10 2 6 3 7 8

样例输出

3

样例说明

值正好相差1的数对包括(2, 3), (6, 7), (7, 8)。
评测用例规模与约定
  1<=n<=1000,给定的整数为不超过10000的非负整数。

测试数据通过了。一次就过, 真开心哈哈哈,虽然很简单

在这里插入图片描述

#include<iostream>
#include <algorithm>
#include <sstream>
#include <cstring>
#include<vector>
using namespace std;
int n;
int arr[10010];
int main()
{
	cin>>n ;
	int k=0 ;
	for( int i=0 ; i<10010 ; i++)
	{
		arr[i]=0 ;
	}
	for( int i=0 ; i<n ; i++ )
	{
		cin>>k ;
		arr[k]+=1 ;
	}
	int sum=0;
	for(int i=0 ; i<10010 ; i++)
	{
		if(arr[i]!=0)
		{
			int j=i;
			while(arr[j]!=0)
			{
				j++;
			}
			sum+=(j-i-1);
			i=j;
		}
	}
	cout<<sum<<endl;
	return 0;
}

2014年 9月 第二题 画图

测试数据通过了。一次就过, 真开心哈哈哈,虽然很简单
问题描述

在一个定义了直角坐标系的纸上,画一个 (x1,y1)到 ( x2 , y2 ) 的矩形指将横坐标范围从x1到x2,纵坐标范围从y1到y2之间的区域涂上颜色。
  下图给出了一个画了两个矩形的例子。第一个矩形是(1,1) 到 (4, 4),用绿色和紫色表示。第二个矩形是(2, 3)到(6, 5),用蓝色和紫色表示。图中,一共有15个单位的面积被涂上颜色,其中紫色部分被涂了两次,但在计算面积时只计算一次。在实际的涂色过程中,所有的矩形都涂成统一的颜色,图中显示不同颜色仅为说明方便。
在这里插入图片描述
给出所有要画的矩形,请问总共有多少个单位的面积被涂上颜色。

输入格式

输入的第一行包含一个整数n,表示要画的矩形的个数。
  接下来n行,每行4个非负整数,分别表示要画的矩形的左下角的横坐标与纵坐标,以及右上角的横坐标与纵坐标。

输出格式

输出一个整数,表示有多少个单位的面积被涂上颜色。

样例输入

2
1 1 4 4
2 3 6 5

样例输出

15

评测用例规模与约定

1<=n<=100,0<=横坐标、纵坐标<=100。
在这里插入图片描述

#include<iostream>
#include <algorithm>
#include <sstream>
#include <cstring>
#include<vector>
using namespace std;
int n;
int a[102][102];
int main()
{
	cin>>n ;
	getchar();
	int x1=0,y1=0,x2=0,y2=0;
	int k=0 ;
	for( int i=0 ; i<102 ; i++)
	{
		for( int j=0 ; j<102 ; j++)
		{
			a[i][j]=0;
		}
	}
	for( int i=0 ; i<n ; i++ )
	{
		cin>>x1>>y1>>x2>>y2;
		for(int j =x1 ;j<x2;j++)
		{
			for(int k=y1;k<y2;k++)
			{
				 a[j][k]=1;
			} 
	    }
	}
	int sum=0;
    for(int j =0 ;j<101;j++)
	{
		for(int k=0;k<101;k++)
		{
			if(a[j][k])
			{
				sum+=1;
			}
		}
    }
	cout<<sum<<endl; 
	return 0;
}

2014年 9月 第三题 字符串匹配

问题描述

给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行。你的程序还需支持大小写敏感选项:当选项打开时,表示同一个字母的大写和小写看作不同的字符;当选项关闭时,表示同一个字母的大写和小写看作相同的字符。

输入格式

输入的第一行包含一个字符串S,由大小写英文字母组成。
  第二行包含一个数字,表示大小写敏感的选项,当数字为0时表示大小写不敏感,当数字为1时表示大小写敏感。
  第三行包含一个整数n,表示给出的文字的行数。
  接下来n行,每行包含一个字符串,字符串由大小写英文字母组成,不含空格和其他字符。

输出格式

输出多行,每行包含一个字符串,按出现的顺序依次给出那些包含了字符串S的行。

样例输入
		Hello
		1
		5
		HelloWorld
		HiHiHelloHiHi
		GrepIsAGreatTool
		HELLO
		HELLOisNOTHello
样例输出
		HelloWorld
		HiHiHelloHiHi
		HELLOisNOTHello

样例说明
  在上面的样例中,第四个字符串虽然也是Hello,但是大小写不正确。如果将输入的第二行改为0,则第四个字符串应该输出。
评测用例规模与约定
  1<=n<=100,每个字符串的长度不超过100。

不知道是不是还有一些情况漏考虑了, 所以只拿了 70分 。

在这里插入图片描述
看完别人AC的代码 , 有点离谱, 好多函数,不会调用。 全部自己手写。

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
    string s;//短字符串
    cin >> s;
    int flag;
    cin >>flag;//标记大小写是否敏感
    int n;
    cin >> n;
    while(n--)
    {
        string line;//长字符串
        cin >> line;
        if(flag && line.find(s) != -1)// 大小写敏感
            cout << line << endl;
        if(!flag)//大小写不敏感
        {
            string l = line;
            transform(s.begin(), s.end(), s.begin(), ::toupper);// 转换成小写
            transform(line.begin(), line.end(), line.begin(), ::toupper);
            if(line.find(s) != -1) // 找到就输出
                cout << l << endl;
        }
    }
}

于是我又修改 了,我的代码。发现 可以拿满分 了
其中在for循环里面给 index赋值为0 很重要。(在后面debug的时候才意识到的)
在这里插入图片描述

2014年12月第一题门禁系统

问题描述

涛涛最近要负责图书馆的管理工作,需要记录下每天读者的到访情况。每位读者有一个编号,每条记录用读者的编号来表示。给出读者的来访记录,请问每一条记录中的读者是第几次出现。

输入格式

输入的第一行包含一个整数n,表示涛涛的记录条数。
  第二行包含n个整数,依次表示涛涛的记录中每位读者的编号。

输出格式

输出一行,包含n个整数,由空格分隔,依次表示每条记录中的读者编号是第几次出现。

样例输入

5
1 2 1 1 3

样例输出

1 1 2 3 1

评测用例规模与约定

1≤n≤1,000,读者的编号为不超过n的正整数。
一次过
 在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
int n;
int a[1001];
int main()
{
	cin>>n;
	for(int i=0; i<1001;i++)
	{
		a[i]=0;
	}
	int k=0;
	for(int i=0; i<n;i++)
	{
		cin>>k;
		a[k]+=1;
		cout<<a[k]<<" ";
	}
	return 0;
}

2014年12月 第二题 Z字形扫描

问题描述

问题描述
  在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan)。给定一个n×n的矩阵,Z字形扫描的过程如下图所示:
  在这里插入图片描述
 对于下面的4×4的矩阵
  1 5 3 9
  3 7 5 6
  9 4 6 4
  7 3 1 3
  对其进行Z字形扫描后得到长度为16的序列:
  1 5 3 9 7 3 9 5 4 7 3 6 6 4 1 3
  请实现一个Z字形扫描的程序,给定一个n×n的矩阵,输出对这个矩阵进行Z字形扫描的结果。

输入格式

输入的第一行包含一个整数n,表示矩阵的大小。
  输入的第二行到第n+1行每行包含n个正整数,由空格分隔,表示给定的矩阵。

输出格式

输出一行,包含n×n个整数,由空格分隔,表示输入的矩阵经过Z字形扫描后的结果。

样例输入

4
1 5 3 9
3 7 5 6
9 4 6 4
7 3 1 3

样例输出

1 5 3 9 7 3 9 5 4 7 3 6 6 4 1 3

评测用例规模与约定

1≤n≤500,矩阵元素为不超过1000的正整数。

模拟

但是不能很好想到比较合适的方式, 代码写的很乱,于是去看题解了。 
关键是判断运动方向, 一开始以为是要定4个方向, 后来发现就两个方向。
因为移出界了,所以规0操作了。 不是4个方向。边界操作一下就行。

#include <iostream>
using namespace std;
const int N = 510;
int a[N][N];
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            cin >> a[i][j];
    int x = 0, y = 0;
    bool flag = true;//方向一:ture就x--,y++。方向二:false就x++,y--。
    while(x != n || y != n){
        //关于这里为什么需要特判,主要是因为,x和y都有可能大于边界n。
        //(如果还是不清楚的话可以拿我代码模拟下,可以试着删掉下方的if,然后拿例子去调试下)
        if(x < n && y < n)
            cout << a[x][y] << ' ';
        if(flag) x--, y++;
        else x++, y--;

        if(x < 0) {
            x = 0;
            flag = !flag;//x < 0 说明x坐标越界了,把x纠正过来,然后让flag置反
        }
        if(y < 0){
            y = 0;
            flag = !flag;//y < 0 说明y坐标越界了,把y纠正过来,然后让flag置反
        }
    }
    return 0;
}

2014年12月 第三题 集合竞价

问题描述
  某股票交易所请你编写一个程序,根据开盘前客户提交的订单来确定某特定股票的开盘价和开盘成交量。
  该程序的输入由很多行构成,每一行为一条记录,记录可能有以下几种:
  1. buy p s 表示一个购买股票的买单,每手出价为p,购买股数为s。
  2. sell p s 表示一个出售股票的卖单,每手出价为p,出售股数为s。
  3. cancel i表示撤销第i行的记录。
  如果开盘价为p0,则系统可以将所有出价至少为p0的买单和所有出价至多为p0的卖单进行匹配。因此,此时的开盘成交量为出价至少为p0的买单的总股数和所有出价至多为p0的卖单的总股数之间的较小值。
  你的程序需要确定一个开盘价,使得开盘成交量尽可能地大。如果有多个符合条件的开盘价,你的程序应当输出最高的那一个。
输入格式
  输入数据有任意多行,每一行是一条记录。保证输入合法。股数为不超过108的正整数,出价为精确到恰好小数点后两位的正实数,且不超过10000.00。
输出格式
  你需要输出一行,包含两个数,以一个空格分隔。第一个数是开盘价,第二个是此开盘价下的成交量。开盘价需要精确到小数点后恰好两位。
样例输入
buy 9.25 100
buy 8.88 175
sell 9.00 1000
buy 9.00 400
sell 8.92 400
cancel 1
buy 100.00 50
样例输出
9.00 450
评测用例规模与约定
  对于100%的数据,输入的行数不超过5000。
  在这里插入图片描述
大模拟。
因为cancel 对应删除的下标是把每一行都计入的。 不只是看buy 和 sell 的行。 曾经输入过的 cancel 行也包括在内。
第二个可能出现的漏考虑的情况。 取消两次, 就是 没有操作 。 就是要把以前保留的信息再赋值回去。
**这里需要转换一下思维, 就是 可以使用假删除, 就是不删除 ,只是做一个标记, 最后决定要不要输出。 因为如果真的删除之后,会导致无法还原。 **

这里一个点需要做多个标记。 所以使用结构体的形式会节约很多麻烦事情。

#include<iostream>
using namespace std;
int n;
struct record
{
    int type;
    double p;
    int s;
    bool is_del;
}d[5010];
int main()
{
    string type;
    while(cin>>type)
    {
        if(type=="buy")
        {
            double p;
            int s;
            cin>>p>>s;
            d[++n]={1,p,s};
        }
        else if(type=="sell")
        {
            double p;
            int s;
            cin>>p>>s;
            d[++n]={2,p,s};
        }
        else
        {
            int id;
            cin>>id;
            d[id].is_del=1;
            d[++n].is_del=1;// 因为是按序号的, 所以这个位置要做标记,cancel也占一行的。 
        }
    }
    double resp=0;
    long long  ress=0;
    for(int i=1;i<=n;i++)
    {
        if(d[i].is_del==0)
        {
            double p=d[i].p;
            long long  s1=0,s2=0;
            for(int j=1;j<=n;j++)
            {
                if(d[j].is_del==0)
                {
                    if(d[j].type==1&&d[j].p>=p) s1+=d[j].s;
                    else if(d[j].type==2&&d[j].p<=p) s2+=d[j].s;
                }
            }
            long long  t=min(s1,s2);
            if(t>ress||t==ress&&p>resp)
                resp=p,ress=t;
        }
    }
    printf("%.2lf %lld\n", resp, ress);
    return 0;
}

2015年3月 第二题 数字排序

问题描述

给定n个整数,请统计出每个整数出现的次数,按出现次数从多到少的顺序输出。

输入格式

输入的第一行包含一个整数n,表示给定数字的个数。
  第二行包含n个整数,相邻的整数之间用一个空格分隔,表示所给定的整数。

输出格式

输出多行,每行包含两个整数,分别表示一个给定的整数和它出现的次数。按出现次数递减的顺序输出。如果两个整数出现的次数一样多,则先输出值较小的,然后输出值较大的。

样例输入

12
5 2 3 3 1 3 4 2 5 2 3 5

样例输出

3 4
2 3
5 3
1 1
4 1
评测用例规模与约定
  1 ≤ n ≤ 1000,给出的数都是不超过1000的非负整数。
试了一下,只有60分, 不知道哪里的数据没通过。
看了ACWing 的 测试数据。 于是发现竟然通过了。 原来是 边界没搞好, 可以取 的数字范围没有 思考清楚。
在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
int n,m ;// 分别 表示 行 数 和 列 数 
int a[1002][1002];
int b[1002];
int main()
{
	cin>>n;
	int k=0;
	for(int i=0;i<n;i++)
	{
		cin>>k;
		for(int j=1; j<=n;j++)
		{
			if(a[j][k]==0)
			{
				a[j][k]+=1;
				break;
			}
		}
	}
    for(int i=n; i>=0;i--)
    {
    	for(int j =0; j<=1000;j++)
    	{
    		if(a[i][j]>0&&b[j]!=1)
    		{
    			cout<<j<<" "<<i<<endl;
    		    b[j]=1;
			}
		}
	}
	return 0;
}

2015年3月 第三题 节日

问题描述

有一类节日的日期并不是固定的,而是以“a月的第b个星期c”的形式定下来的,比如说母亲节就定为每年的五月的第二个星期日。
  现在,给你a,b,c和y1, y2(1850 ≤ y1, y2 ≤ 2050),希望你输出从公元y1年到公元y2年间的每年的a月的第b个星期c的日期。
  提示:关于闰年的规则:年份是400的整数倍时是闰年,否则年份是4的倍数并且不是100的倍数时是闰年,其他年份都不是闰年。例如1900年就不是闰年,而2000年是闰年。
  为了方便你推算,已知1850年1月1日是星期二。

输入格式

输入包含恰好一行,有五个整数a, b, c, y1, y2。其中c=1, 2, ……, 6, 7分别表示星期一、二、……、六、日。

输出格式

对于y1和y2之间的每一个年份,包括y1和y2,按照年份从小到大的顺序输出一行。
  如果该年的a月第b个星期c确实存在,则以"yyyy/mm/dd"的格式输出,即输出四位数的年份,两位数的月份,两位数的日期,中间用斜杠“/”分隔,位数不足时前补零。
  如果该年的a月第b个星期c并不存在,则输出"none"(不包含双引号)。
样例输入
5 2 7 2014 2015
样例输出
2014/05/11
2015/05/10
评测用例规模与约定
  所有评测用例都满足:1 ≤ a ≤ 12,1 ≤ b ≤ 5,1 ≤ c ≤ 7,1850 ≤ y1, y2 ≤ 2050。

日期问题

本题模拟步骤:
1.从1850年1月1日开始枚举,枚举到y2
2.每一年内枚举月份1-12,算一下每年每月的1号距离1850年1月1日过了多少天(因为要求星期几),过了的天数%7加上1850年1月1日星期二(题目已知条件)的下标(这里下标用0~6表示星期一到星期七)就可以得到某年某月的1日是星期几
3.判断a月份时,求一下a月份的第b个星期c,看下是否存在

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
//日期题套路,12个月,开13个位置,第0个为占位符
int months[13] = {
    0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

//判断某一年是否是闰年的函数
int is_leap(int year)
{
    if(year % 400 == 0 || (year % 4  == 0 && year % 100)) return 1;
    return 0;
}
//求某月有多少天的函数
int get_days(int year, int month)
{
    if(month == 2) return months[month] + is_leap(year); //如果是2月的话返回天数和是否是闰年,如果是闰年2月份+1天
    return months[month]; //不是2月份则直接返回天数即可
}
//星期一到星期日用0~6表示
int main()
{
    int a, b, c, y1, y2;
    cin >> a >> b >> c >> y1 >> y2;
    int days = 0; //表示过了多少天
    for(int year = 1850; year <= y2; year ++ )
        for(int month = 1; month <= 12; month ++ )
        {
            if(year >= y1 && month == a)
            {
                int w = (1 + days) % 7; //先算一下这个月的1月1号是星期几,1850年1月1日是星期二,下标为1
                int cnt = 0; //统计一下当前是枚举到的第几个星期c
                for(int d = 1; d <= get_days(year, month); d ++ ) //枚举一下这个月的所有天
                {
                    if(w == c - 1) //星期的下标从0开始,所以要-1,如星期七的下标是6
                    cnt ++ ;//星期c的个数++
                    if(cnt == b) //如果星期c的个数等于b的话,满足条件,输出
                    {
                        printf("%04d/%02d/%02d\n", year, month, d);
                        break;
                    }
                    w = (w + 1) % 7; //每过一天,星期需要往后错一位
                }
                if(cnt < b) //枚举完这个月后,如果星期c出现的次数小于b,说明没有第b个星期c,输出none
                {
                    puts("none");
                }
            }
            days += get_days(year, month); //这个月过完之后加上这个月过的天数
        }
    return 0;
}

2015年9月 第一、二题都太简单了,不写了。

2015年12月 第2题 消除类游戏

问题描述
  消除类游戏是深受大众欢迎的一种游戏,游戏在一个包含有n行m列的游戏棋盘上进行,棋盘的每一行每一列的方格上放着一个有颜色的棋子,当一行或一列上有连续三个或更多的相同颜色的棋子时,这些棋子都被消除。当有多处可以被消除时,这些地方的棋子将同时被消除。
  现在给你一个n行m列的棋盘,棋盘中的每一个方格上有一个棋子,请给出经过一次消除后的棋盘。
  请注意:一个棋子可能在某一行和某一列同时被消除。
输入格式
  输入的第一行包含两个整数n, m,用空格分隔,分别表示棋盘的行数和列数。
  接下来n行,每行m个整数,用空格分隔,分别表示每一个方格中的棋子的颜色。颜色使用1至9编号。
输出格式
  输出n行,每行m个整数,相邻的整数之间使用一个空格分隔,表示经过一次消除后的棋盘。如果一个方格中的棋子被消除,则对应的方格输出0,否则输出棋子的颜色编号。
样例输入
4 5
2 2 3 1 2
3 4 5 1 4
2 3 2 1 3
2 2 2 4 4
样例输出
2 2 3 0 2
3 4 5 0 4
2 3 2 0 3
0 0 0 4 4
样例说明
  棋盘中第4列的1和第4行的2可以被消除,其他的方格中的棋子均保留。
样例输入
4 5
2 2 3 1 2
3 1 1 1 1
2 3 2 1 3
2 2 3 3 3
样例输出
2 2 3 0 2
3 0 0 0 0
2 3 2 0 3
2 2 0 0 0
样例说明
  棋盘中所有的1以及最后一行的3可以被同时消除,其他的方格中的棋子均保留。
评测用例规模与约定
  所有的评测用例满足:1 ≤ n, m ≤ 30。

解决方法

就是数组边界的问题。

#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int a[31][31];
int flag[31][31];
int main()
{
	cin>>n>>m;
	int sum=0;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			cin>>a[i][j];
		}
	}
	for(int i=0;i<=n;i++)
	{
		for(int j=0;j<=m;j++)
		{
			int k=1;
			while(k<m-j)
			{
				if(a[i][j]==a[i][j+k])
				{
					k++;
					continue;
				}
				else
				{
					break;
				}
			}
			if(k>=3)
			{
				for(int l=0;l<k;l++)
				{
					flag[i][j+l]=1;
				}
			}
			k=1;
			while(k<n-i)
			{
				if(a[i][j]==a[i+k][j])
				{
					k++;
					continue;
				}
				else
				{
					break;
				}
			}
			if(k>=3)
			{
				for(int l=0;l<k;l++)
				{
					flag[i+l][j]=1;
				}
			}	
		}
    }
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(flag[i][j]!=1)
			{
				cout<<a[i][j]<<" ";
			}
			else
			{
				cout<<0<<" ";
			}
		}
		cout<<endl;
	} 
	return 0;
}

在这里插入图片描述

2016年4月 第一题 折点计数。 因为边界问题,卡了很久。

在这里插入图片描述
问题描述
  给定n个整数表示一个商店连续n天的销售量。如果某天之前销售量在增长,而后一天销售量减少,则称这一天为折点,反过来如果之前销售量减少而后一天销售量增长,也称这一天为折点。其他的天都不是折点。如下图中,第3天和第6天是折点。
  在这里插入图片描述
看了一些测试数据才调试通过的。

#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int a[1010];
int main()
{
	cin>>n;
	int sum=0;
	for(int i=0;i<n;i++)
	{
		cin>>a[i]; 
	}
	int k=0,i=0;
	while( k < n-1 )
	{
		while(a[k]<a[k+1]&&k<n)
		{
			k++;
		}
		if(k==n-1||k==n)
		{
			break;
		}
		if(k-i>=1)
		{
			//cout<<"k:"<<k<<endl;
			sum+=1;
			i=k;
		}    
		while(a[k]>a[k+1]&& k<n)
		{
			k++;
		}
		if(k==n-1||k==n)
		{
			break;
		}
		if(k-i>=1)
		{
			//cout<<"k:"<<k<<endl;
			sum+=1;
			i=k-1;
		}
		
	}
	cout<<sum<<endl;
	return 0;
}
算法二: 就只关注折点附近的两个点以及折点本身。
#include<iostream>
using namespace std;
int main(){
    int n;
    cin>>n;
    int a[n];
    for(int i=0;i<n;i++)
    cin>>a[i];
    int b=0;
    for(int i=1;i<n-1;i++)//去掉头和尾,循环n-2次就行了
    {
        if(a[i]<a[i-1]&&a[i]<a[i+1])   b++;
        else if(a[i]>a[i-1]&&a[i]>a[i+1])  b++;
    }
    cout<<b;
}

2016年4月 第二题俄罗斯方块。 (没做出来)

在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
const int N = 20;
int g[N][N], s[N][N], h[4][4], k;
bool draw(int x, int y){
    memcpy(s, g, sizeof(s));
    for(int i = 0; i < 4; i ++){
        for(int j = 0; j < 4; j ++)
            if(h[i][j]){
                int a = i + x, b = j + y;
                s[a][b] ++;
                if(s[a][b] == 2) return true;
            }
    }
    return false;
}
int main(){
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 10; j ++) cin >> g[i][j];
    }
    for(int i = 0; i < 10; i ++) g[15][i] = 1;

    for(int i = 0; i < 4; i ++){
        for(int j = 0; j < 4; j ++) cin >> h[i][j];
    }
    cin >> k;

    for(int i = 0; ; i ++){
        if(draw(i, k - 1)){
            draw(i - 1, k - 1);
            break;
        }
    }
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 10; j ++) cout << s[i][j] << " ";
        puts("");
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值