CSU(ACM入门)1340 A Sample Problem 1341 String and Arrays 1342 Double 1343 Long Long 1344 Special Judge

来源:互联网 发布:java中时间格式 编辑:程序博客网 时间:2024/05/16 12:21

这是一个系列:ACM入门示例(第一季)

CSU 1340: A Sample Problem


Description

   My girlfriend loves 7 very much, she thinks it is lucky! If an integer contains one or more 7, she will think it is lucky too!

Input

    The first line has one integer T (1 <= T <= 100), means there are T test cases.
    For each test case, there is only one line with an integer X (1 <= X <= 109).

Output

    For each test case, if X contains one or more 7, output “Lucky” (without quotation marks), otherwise output “Unlucky” (without quotation marks).

Sample Input

471156817171

Sample Output

LuckyUnluckyUnluckyLucky

HINT

    我们主要通过这个题目来感受一下一个完整的ACM竞赛的题目应当是什么样的。

    ACM竞赛题目本身的“格式”很明显:

    第一部分是对题目的描述,会交待题目的背景,以及你需要编程完成哪些任务等等。

    第二部分是对输入的约定,会告诉你题目是否有多组测试数据,每组测试数据的格式,每个参数的类型(整数,浮点数,字符串等)以及取值范围等等。

    第三部分是对输出的约定,也就是你的程序应当输出哪些内容以及具体的格式是什么。

    第四部分是一些样例,这些例子只是向你展示一部分测试数据以及对应的答案,目的是更好地帮助你理解题意,以及了解对输出格式的要求等。

    大家也许会和我当初刚接触ACM时一样心中充满了疑问,在这里我就为大家列举几个常见的问题:

    <1> 如果题目有多组数据,我们是不是也要像样例那样把所有的结果先都计算出来,然后再一起输出?

    这个是没必要的,你完全可以每处理完一组数据之后就立刻将这组数据对应的结果输出出来。

    但如果你是手动去敲这些样例的话,你会发现在屏幕上输入和输出是混在一起的,并不像样例那样输入和输出是分离的,这样会不会不符合要求呢?那么请看<2>,相信你的疑问就会被解答了。

    <2> OJ (Online Judge)怎么知道我的程序是不是写对了?

    OJ并不会去智能化地分析你的代码正确与否,而是用另一种巧妙的思路来判断你的程序是否符合要求:“喂”给你的程序一些输入数据(就像你在键盘上敲一些东西后敲回车一样),之后再将你的程序“产出”的东西(就像你在屏幕上看到的你的程序输出的东西一样)和标准的答案进行对比,如果你的程序得到的答案和标准的答案一模一样,那么就算你通过了这个题目。

    因此,即使你手动输入样例时屏幕上的输入和输出是混在一起也没关系,只要你的程序“产出”的东西和标准答案一模一样就可以了。

    也正是这种评判机制的关系,你不应当让程序打印一些提示语句,如“Please input two integers”等等,因为凡是你的程序输出的内容都会被拿来和标准答案进行对比,一旦输出了类似这样的题目没有要求输出的语句,就会和标准答案大相径庭了,自然就会认为你的程序是不正确的了。

    值得一提的是,尽管这样的评判机制似乎并不能完美地知道你的程序究竟是不是对的(想想看为什么?),不过如果测试数据足够强大的话,“你的程序是对的”的概率就会很高很高了。

    <3> 为什么我的程序通过了所有的样例,但是最后还是过不了这个题目?

    题目的样例只是一小部分测试数据,目的是为了更好地帮助你理解题意,以及了解对输出格式的要求等,因此通过了样例并不能够代表能通过所有的测试数据。只有通过了所有的测试数据才能得到“Accept”,也就是通过了这个题目。

    <4> 每组测试数据的答案输出完之后,是否都要换行?

    是的,即使题目里面没有明确说明,我们也应该在每组测试数据的答案输出完之后再输出一个“换行”(也就是“\n”),就像样例示意的那样。

    接下来我们谈谈这个题目的思路吧,相信大家应该都想到算法了:只要依次判断读入的整数的每一位是否是7就可以了。如果觉得代码写起来会有困难的话,可以参考一下我在下面给出的示例代码,不过最后一定要按自己的思路写一个完整的代码(编写自己的代码时就不要再参考示例代码了,要一气呵成~)并获得“Accept”哟!O(∩_∩)O~


以上皆为题目。

以下是代码:

#include<iostream>#include<cstring>using namespace std; char c[10]; int main(){    ios_base::sync_with_stdio(false);    int t;    cin >> t;    while (t--)    {        cin >> c;        int f = 0;        for (int i = 0; i < strlen(c); i++)if (c[i] == '7')f = 1;        if (f)cout << "Lucky" << endl;        else cout << "Unlucky" << endl;    }    return 0;}

ACM的题目就是这样,明明输入的是1个整数,但是我完全可以看成输入的就是一个个字符,只需要看每个字符是不是7,而不需要任何数学计算。


CSU 1341: String and Arrays

题目:

Description

    有一个N*N的字符矩阵,从上到下依次记为第1行,第2行,……,第N行,从左至右依次记为第1列,第2列,……,第N列。
    对于这个矩阵会进行一系列操作,但这些操作只有两类:
    (1) R: 将矩阵逆时针旋转90度;
    (2) P x y: 将此时第x行第y列的字符打印出来,其中1 <= xy <= N

Input

    输入数据的第一行包含一个整数T (1 <= T <= 20),表示接下来一共有T组测试数据。
    对于每组测试数据,第一行包含一个整数N (1 <= N <= 300),含义同上。接下来一共有N行,每行均包含N个大写字母,描述了这个矩阵的初始情况。再接下来一行包含一个整数M (1 <= M <=10000)表示一共对矩阵进行了M次操作。接下来M行,每行均包含一个符合上述格式的操作,依次描述了这M个操作。

Output

    对于每个第(2)类操作,用一行打印出指定位置的字符。
    相邻的两组测试数据中间用一个空行隔开。

Sample Input

32ABCD3P 1 1RP 1 12ABCD4RRP 2 1P 1 23ABCDEFGHI5P 3 3RP 3 3RP 3 3

Sample Output

ABBCIGA

HINT


    这个题目主要练习如何读入字符串,以及对数组的使用。


    初始矩阵直接用读入字符串的方式读入就可以了,但是每个操作要如何读入呢?先用getchar(x)或者scanf(“%c”, &x)读入一个字符,然后再根据读入的字符判断是否要再读入两个整数? 


    其实getchar(x)或者scanf(“%c”, &x)有的时候很令人头疼(也许只有你亲自尝试过才能体会到), 因为他不仅可以读入一个字母,也可以读入空格、换行等等,因此如果我们不细加控制的话,scanf(“%c”, &x)很可能读到的未必是我们想要的那个字符。但scanf(“%s”, s)就不一样了,会忽略掉空格、换行等等,就像我们使用scanf(“%d”, &x)读整数那样。因此即使只有一个字母,我们也更推荐使用scanf(“%s”, s)的形式去读入,然后s[0]自然就是那个字符了。


    这个题目具体要怎么做呢?看似直接模拟两种操作就好了,打印字符最简单了,而旋转矩阵也不是很麻烦,不过可能要借助一个“中间矩阵”来完成旋转更好一些。但问题来了,如果每个R我们都进行旋转的话,实际上代价是很大的,对于这个题目而言会超时。也许你会机智地想到把相邻的R操作合并成一个,这样代价会小一点,是的,但是这样并不能解决根本问题,比如R和P是交替出现的。


    但其实我们可以做到只要旋转3次矩阵,你想到了吗?


    或者也许我们根本没必要按题意来去旋转矩阵?是否可以直接找到要打印的字符在初始矩阵中的位置呢?


    最后注意相邻两组测试数据之间要打印一个空行,就像在“A Sample Problem”中说的那样,OJ对于输出的格式要求是很严格的。


    同样的,我会给出示例代码,如果你觉得编写代码的时候有些困难的话可以参考一下示例代码,但最后一定要按自己的思路一气呵成一份完整的代码并获得“Accept”哟~



解析写的太好了,我就直接上代码好了。

代码:

#include <iostream>using namespace std;char c[301][301];int t, n, m, sum, x, y;char f(){if (sum == 0)return c[x][y];if (sum == 1)return c[n + 1 - y][x];if (sum == 2)return c[n + 1 - x][n + 1 - y];return c[y][n + 1 - x];}int main(){ios_base::sync_with_stdio(false);char ch;cin >> t;while (t--){cin >> n;for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)cin >> c[i][j];cin >> m;sum = 0;while (m--){cin >> ch;if (ch == 'R')sum--;//不是加,是减else{sum = ((sum % 4) + 4) % 4;cin >> x >> y;cout << f() << endl;}}cout << endl;}return 0;}

CSU 1342:Double

题目:

Description

    有一个由M个整数组成的序列,每次从中随机取一个数(序列中每个数被选到的概率是相等的)累加,一共取N次,最后结果能被3整除的概率是多少?

Input

    输入包含多组数据。
    对于每组测试数据,第一行包含两个整数MN (1 <= M <= 100, 1 <= N <= 30),含义同上。接下来一行包含M个在[1, 100]范围内整数,依次描述了这个序列中的各个整数。

Output

    对于每组数据,用“Case XY”的格式输出答案,其中X表示是第几组测试数据(从1开始),Y表示最后结果能被3整除的概率,四舍五入保留8位小数。

Sample Input

2 21 21 241 345 104 5 3 1 5

Sample Output

Case 1: 0.50000000Case 2: 0.00000000Case 3: 1.00000000Case 4: 0.33333340

Hint

    这个题目主要是想推荐大家用“double”处理浮点数,而尽量不要用“float”,因为“float”的精度偏低,往往不能满足题目的精度要求,所以在ACM竞赛中索性可以直接使用“double”去处理浮点数问题。“double”用“%lf”控制读入,“%f”控制输出。

    我们先解决一下其他的细节问题再来讨论这个题的思路。

    首先,这个题目也是有多组数据的,但不像“A Sample Problem”那样直接给出了数据的组数,那么要怎么处理呢?这时我们一般采用类似while(scanf(“%d%d”, &M, &N) != EOF){}这样的代码来处理,“!= EOF”是关键,至于他的具体含义就不过多介绍了,总之有了这个框架之后,我们直接在while循环里面写我们处理每组数据的代码就可以了。这时你可能会有这样的疑问:如果这么写代码的话,那么我在手动输入样例的时候怎么才算结束输入呢?Ctrl + Z,然后回车就OK了!

    其次,保留8位小数怎么处理呢?一般在ACM竞赛里面,如果没有明确说明具体怎么处理(比如要用“去尾法”),或者说让“四舍五入”,我们都采用类似printf(“%.8f”, x)的形式保留指定位数的小数。至于使用C++中的cout输出的同学,请自己查阅控制小数位数的相关资料。

    接下来我们就分析这个题目怎么做吧。

    首先,最后能不能被3整除,实际上和最后取出的整数之和(不妨记这个和为“S”)模3的结果有关系。所谓的“模”就是C/C++中的运算符“%”,就是“除以某个数取余”的意思。如果S%3==0,那么就是能被3整除,如果S%3==1或者S%3==2,那么就不能被3整除。也就是说我们要计算的就是S%3==0的概率。

    我们不妨分析一下第四个样例。取十次有点多,我们先取一次看看。

    取一次的话,模3得0的数只有3,所以S%3==0的概率就是0.2(记这个概率为p0),模3得1的数有两个:4和1,所以S%3==1的概率就是0.4(记这个概率为p1),同样的,模3得2的数有两个:5和5,所以S%3==2的概率也是0.4(记这个概率为p2)。

    再分析一下取两次的情况?

    取两次的话S%3==0的概率要怎么算呢?p0*p0 + p1*p2 + p2*p1 = 0.36。这么算的含义是什么呢?因为要保证最后S%3==0,那么如果第一次取出的数模3得0的话,第二次取的数必须也是模3得0,同样的,如果第一次取出的数模3得1的话,那么第二次取出的数必须是模3得2,如果第一次取出的数模3得2的话,那么第二次取出的数必须模3得1。这样我们就得到了上面的式子。同理,我们可以计算S%3==1的概率为p0*p1 + p1*p0 + p2*p2 = 0.32,S%3==2的概率也是0.32。

    再分析一下取三次的情况?

    取三次的话S%3==0的概率应当是0.36*p0 + 0.32*p2 + 0.32*p1。这么算的意义想必大家已经想到了,因为我们要保证最后S%3==0,那么如果前两次取出的数之和模3得0的话,那么第三次取出的数必须也是模3得0,同样的,如果前两次取出的数之和模3得1的话,那么第三次取出的数必须是模3得2,如果前两次取出的数之和模3得2的话,那么第三次取出的数必须是模3得1。同理,我们也很容易写出S%3==1以及S%3==2的概率要怎么算。

    分析到这里想必大家应该已经想到第四次,第五次,一直到第十次要怎么计算了吧?用类似的办法,根据第三次的结果就可以计算出第四次的结果,根据第四次的结果就可以算出第五次的,等等。这样即使一直算到一百次也不成问题!for循环100次就OK了。

    同样的,我会给出示例代码,如果你觉得把上面的思路转化成代码有些困难的话可以参考一下示例代码,但最后一定要按自己的思路一气呵成一份完整的代码并获得“Accept”哟~




其实这个题目可以算是概率DP。

代码估计都差不多,我用了一点空间优化,然而并没有什么区别。

代码:

#include <iostream>#include<iomanip>using namespace std;double p1, p2, p3;int a, b, c;void get_p(){int s = a + b + c;double x1 = a*p3 + b*p2 + c*p1;double x2 = b*p3 + c*p2 + a*p1;p3 = (a*p2 + b*p1 + c*p3) / s;p2 = x2 / s;p1 = x1 / s;}int main(){ios_base::sync_with_stdio(false);int m, n, i = 1, num;while (cin >> m >> n){a = 0, b = 0, c = 0;while (m--){cin >> num;if (num % 3 == 1)a++;else if (num % 3 == 2)b++;else c++;}p1 = 0, p2 = 0, p3 = 1;while (n--)get_p();cout << "Case " << i++ << ": " << fixed << setprecision(8) << p3 << endl;}return 0;}

CSU 1343: Long Long
题目:

Description

    现在有两个单调递增序列,第一个序列有N个整数,第二个序列有M个整数,现在你可以从第一个序列中选一个数x,然后从第二个序列中选一个数y,那么有多少种情况满足x+y<=K呢?

Input

    输入包含多组数据。
    对于每组测试数据,第一行包含两个整数NM , K (1 <= NM <= 105, 1 <= K <= 109),含义同上。接下来一行包含N个在[1, 109]范围内的整数,依次描述了第一个序列中的各个整数。再接下来一行包含M个在[1, 109]范围内的整数,依次描述了第二个序列中的各个整数。

Output

    对于每组数据,输出有多少种情况满足x + y <= K

Sample Input

2 3 53 73 5 74 5 61 2 3 41 2 3 4 54 5 91 2 3 41 2 3 4 5

Sample Output

01420

Hint

    其实如果两个序列并不是有序的,我们也可以先通过排序使其变得有序,然后就和现在的这个题目一样了,不过由于这个题目并不是为了介绍排序,所以干脆就直接给出有序的序列了。

    由于int的范围有限,当要读入的数或者要输出的数超出了int的表示范围但没有超出long long的表示范围时,我们就要使用long long了。如果OJ的服务器是Linux的,那么用“%lld”(前面是两个小写的L)来控制long long的读入和输出,而如果OJ的服务器是Windows的,则需要用“%I64d”(前面是一个大写的i)来控制long long的读入和输出,这一点一般会在OJ的“F.A.Q”有所说明的。由于我们的服务器是采用的Linux操作系统,所以使用“%lld”就OK了。

    比如这个题目,极端情况就所有情况都满足,也就是N*M中情况,当N和M都取极限值时,显然结果就需要用long long来保存并输出了。

    那么这个题目具体要怎么做呢?

    也许你一眼就看出来了,两层for循环就可以搞定嘛,不过很可惜这样估计要妥妥超时,要不试一下?

    我们不妨仔细梳理一下思路,两层for循环做法的基本思想应当是首先枚举第一个序列中拿出的数x,然后再寻找第二个序列中有多少个整数y满足x+y<=K。其实我们不难发现,每次只要找到满足x+y<=K最大的y就可以了,不妨记为max_y,那么max_y以及max_y之前的数是一定都满足x+y<=K的,这样根据max_y所在的位置,我们就能直接计算出对于当前枚举到的x有多少个y满足x+y<=K了。而找这个max_y的过程真的需要一层for循环吗?你可能已经想到了,我们完全可以用“二分”的方法去找这个max_y,这样就能快很多了。如果你对“二分”还没有任何概念也没关系,因为我们还有另外一种办法也可以达到这个目的。

    我们不妨设先后枚举到的两个相邻的x分别为x1和x2,并设x1对应的max_y为max_y1,x2对应的max_y为max_y2,由于x1<x2,我们可以得到max_y2<=max_y1。但这个有什么用呢?当然有用了,我们在找max_y2的时候就可以直接从max_y1开始递减去找了。如果再枚举到x3呢?那么一样有max_y3<=max_y2,我们直接从max_y2开始递减去找max_y3。不过也许你可能会怀疑了:这样真的能比两层for循环快吗?好像这样也是两个变量不停地在循环呀?

    至于为什么会快还是交由你自己来思考吧,如果你对刚刚说的思路还没有什么概念的话不妨结合下面我给出的示例代码来看一下。


代码:

#include <iostream>#include<algorithm>using namespace std;int listn[100000];int listm[100000];int main(){ios_base::sync_with_stdio(false);int n, m, k, temp;while (cin >> n >> m >> k){for(int i = 0; i < n; i++)cin >> listn[i];for (int i = 0; i < m; i++){cin >> temp;listm[m - 1 - i] = k - temp;}long long sum = m;sum *= n;int key = 0;for (int i = 0; i < n; i++){while (listm[key] < listn[i] && key < m)key++;sum -= key;}cout << sum << endl;}return 0;}

CSU 1344: Special Judge
题目:

Description

Given a positive integer n, find two non-negative integers ab such that a2 + bn.

Input

The first line contains the number of test cases T (1 <= T <= 1000).
For each test case, there is only one line with an integer n (1 <= n <= 109) as defined above.

Output

For each test case, output two integers ab separated by a single space such that a2 + b2 = n. If there are multiple solutions, anyone will be accepted. If there is no solution, output “-1” (without quotation marks) instead.

Sample Input

42344

Sample Output

1 1-10 22 0

Hint

还记得我们在“A Sample Problem”中说到的OJ的判题形式吗?这个题主要让我们来熟悉一下OJ的另一种名为Special Judge的判题形式。所谓的Special Judge是指OJ将使用一个特定的程序来判断我们提交的程序的输出是不是正确的,而不是单纯地看我们提交的程序的输出是否和标准输出一模一样。

一般使用Special Judge都是因为题目的答案不唯一,更具体一点说的话一般是两种情况:

(1)题目最终要求我们输出一个解决方案,而且这个解决方案可能不唯一。比如这个题目就是这样的,只要求我们输出一组a、b满足a^2 + b^2 = n,而对于不同的n,可能有多组a、b满足这一条件,像n=4时,就有a=2、b=0和a=0、b=2这两组解,这时我们输出任意一组解都可以。

(2)题目最终要求我们输出一个浮点数,而且会告诉我们只要我们的答案和标准答案相差不超过某个较小的数就可以,比如0.000001。这种情况我们只要保证我们的中间运算过程尽可能精确就可以了,并且最后输出答案的时候保留的小数位数足够多就行了,比如如果要求我们的答案和标准答案相差不超过0.01,那么一般我们保留3位小数、4位小数等等都是可以的,而且多保留几位小数也没什么坏处。

为了能让大家更清楚的了解Special Judge的原理,这里我把这个题目的执行Special Judge功能的程序的源代码贴出来了。




大家也许发现了,这个执行Special Judge功能的程序并不那么完美,比如我们在输出a和b的时候中间如过用两个空格分隔a和b的话也是可以通过这个题目的,但是这样实际上是不符合题目在Output中约定的输出格式的。我在这里之所以用了这样一个不是很完美的Special Judge,主要原因是写一个十分完美的Special Judge是要花费很大“力气”的,而且有些时候输出的格式并不不是这个题目的核心,更重要的应该是我们输出的a和b应当是符合要求的。不过大家千万不能凡是遇到Special Judge的题目就不管输出格式了,因为并不是所有写Special Judge程序的人都会像我这样偷工减料,我们最好还是要严格按照题目的要求来输出答案。

接下来我们分析一下这个题目应当怎么解决。

最容易想到的办法就是枚举所有可能的a、b,看是否存在一组a、b满足a^2 + b^2 = n。那么所有的可能的a、b有多少呢?最起码的一个限制条件就是a<=n且b<=n,不过我们还能让限制条件更“精确”一些,就是a<=sqrt(n)且b<=sqrt(n),虽然这样枚举量减少了不少,但是我们枚举所有可能的a、b的话还是要枚举sqrt(n)*sqrt(n) = n次,由于n最大可能是10^9,这样的枚举量还是不能承受。

但其实我们是没有必要枚举b的,因为当a确定时b=sqrt(n - a^2),我们可以将b直接求出来,如果b是整数的话,我们就算找到了一组解。这样如果我们枚举完所有可能的a后还没找到一组解的话,就可以说明是无解了。

如果觉得代码写起来会有困难的话,可以参考一下我在下面给出的示例代码,不过最后一定要按自己的思路写一个完整的代码(编写自己的代码时就不要再参考示例代码了,要一气呵成~)并获得“Accept”哟!O(∩_∩)O~

这个程序在计算b时实际上是在计算满足b^2 <= n - a^2的最大的b,之后再判断是否满足a^2 + b^2 = n就可以了。计算b的代码是b=(int)sqrt(n - a^2 + 0.5),之所以+0.5,一个原因是因为这样不会改变计算结果(也就是说这样求出来的仍是满足b^2 <= n - a^2的最大的b),另一个原因是怕出现浮点数误差(也就是说假如真的存在整数b满足a^2 + b^2 = n,通过(int)sqrt(n - a^2)未必能计算出正确的b,因为毕竟sqrt()这个函数是浮点数运算,万一算出的b丢失了一些精度那么向下取整之后就会变成b-1了)。


我的代码,讲道理应该是没错的,虽然wrong answer,还是贴出来吧。

代码:

#include <iostream>#include<math.h>using namespace std;int main(){int t, n;cin >> t;while (t--){cin >> n;int i = 0, j = -1;bool b = true;for (; i*i <= n; i++){j = int(sqrt(n - i*i) + 0.01);if (j*j == n - i*i){b = false;break;}}if(!b)cout << i << " " << j;else cout << -1;cout << endl;}return 0;}

2 0
原创粉丝点击