2014苏州大学新生赛第二场(12.10)题目解析
2014-12-11 01:13:47
小结:嘛,这场据说比较难,该卡的地方都卡了.... 额 这么说也不为过,#1六题,有点低于预期吧,其实题目难度还好,就是时间太短,正常的应该比5小时,没人应该还能过1、2题,所以这个结果还是可以理解的,恩恩,学弟学妹们很给力!^.^ 好了,下面是简单的题目解析。
1001(灰度矩阵)
这题应该 no problem,按行列顺序读入g[i][j],然后按照g[j][i]输出,唯一的 trick 点就是每行最后一个数后面没有空格,这导致了很多人PE。
1 /************************************************************************* 2 > File Name: 1009st.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Thu 04 Dec 2014 02:26:16 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <stack> 16 #include <queue> 17 #include <iostream> 18 #include <algorithm> 19 using namespace std; 20 #define lp (p << 1) 21 #define rp (p << 1|1) 22 #define getmid(l,r) (l + (r - l) / 2) 23 #define MP(a,b) make_pair(a,b) 24 typedef long long ll; 25 const int INF = 1 << 30; 26 27 int n; 28 int g[50][50]; 29 30 int main(){ 31 while(scanf("%d",&n) != EOF){ 32 for(int i = 1; i <= n; ++i){ 33 for(int j = 1; j <= n; ++j){ 34 scanf("%d",&g[i][j]); 35 } 36 } 37 for(int i = 1; i <= n; ++i){ 38 printf("%d",g[1][i]); 39 for(int j = 2; j <= n; ++j){ 40 printf(" %d",g[j][i]); 41 } 42 printf("\n"); 43 } 44 } 45 return 0; 46 }
1002(百度之星)
出这题的灵感来自高中数学老师的趣题,仔细思考可以发现从(0,0)到(a,b)的方案就是C(a + b,a),原因是:你总共走a+b步,并且要向上走b步,向右走a步,所以只要在a+b步里面选择a步向右走即可(或者选择b步向上走,同理,答案为C(a+b,b),一样的。)
所以先从(0,0)到(k,k)的方案数 a1 = C(2k,k);从(k,k)到(n,m)的方案数 a2 = C(n - k + m - k,n - k),最终答案为a1 × a2
那么怎么求组合数呢?可以用公式:C(n,m) = C(n - 1,m) + C(n - 1,m - 1) 来方便的求出(具体看代码,非常经典的求法),这题我刻意卡了数据范围,所以要用long long。
1 /************************************************************************* 2 > File Name: 1002st.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Mon 01 Dec 2014 07:02:13 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <stack> 16 #include <queue> 17 #include <iostream> 18 #include <algorithm> 19 using namespace std; 20 #define lp (p << 1) 21 #define rp (p << 1|1) 22 #define getmid(l,r) (l + (r - l) / 2) 23 #define MP(a,b) make_pair(a,b) 24 typedef long long ll; 25 const int INF = 1 << 30; 26 27 ll c[100][100]; 28 int n,m,k; 29 30 void Pre(){ 31 for(int i = 0; i <= 40; ++i){ 32 for(int j = 0; j <= i; ++j){ 33 c[i][j] = (i == 0 || j == 0) ? 1 : c[i - 1][j] + c[i - 1][j - 1]; 34 } 35 } 36 } 37 38 int main(){ 39 Pre(); 40 while(scanf("%d%d%d",&n,&m,&k) != EOF){ 41 printf("%I64d\n",c[2 * k][k] * c[n + m - 2 * k][n - k]); 42 } 43 return 0; 44 }
1003(翔神的三角形)
此题非常抱歉...题目出错了...是我们的失误,再次道歉!题面已经改成求三角形的最大可能周长,那么就是排序后扫描一遍了。 (>.< 羞愧死了)
1004(优美的数)
数学题,先来讲讲正解:看到 (a + b + ab) 第一反应:因式分解,k = a + b + ab = (a + 1) * (b + 1) - 1(如果a,b均为“优美的数”,那么k也是“优美的数”),移项得:k + 1 = (a + 1) * (b + 1),在题面中我们知道 1 是“优美的数”,带入式子得:3 是“优美的数”,然后把a=1,b=3带入式子,得到:7 + 1 = 2 * 4 = 8,怎么样,发现规律了吧,就是“优美的数” + 1 的因子只能是2、4,更进一步:“优美的数” + 1 就是 2 的幂!那么只要用 2 去不断地除 “优美的数” + 1,看看最后的结果是否为1,如果为1那么YES,否则NO。
(当然,有些人都看出来了,n的范围在10^9以内,而2^30 > 10^9,也就是说10^9内只有30个左右“优美的数”,所以通过暴力计算打个表也能做)
1 /************************************************************************* 2 > File Name: 1004st.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Wed 03 Dec 2014 09:32:55 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <stack> 16 #include <queue> 17 #include <iostream> 18 #include <algorithm> 19 using namespace std; 20 #define lp (p << 1) 21 #define rp (p << 1|1) 22 #define getmid(l,r) (l + (r - l) / 2) 23 #define MP(a,b) make_pair(a,b) 24 typedef long long ll; 25 const int INF = 1 << 30; 26 27 int main(){ 28 int n; 29 while(scanf("%d",&n) != EOF){ 30 ++n; 31 while(n % 2 == 0) n /= 2; 32 if(n == 1) printf("YES\n"); 33 else printf("NO\n"); 34 } 35 return 0; 36 }
1005(优美的数列)
从题意可知,“优美的数列”实际上就是1,2,3,...,n的一个排列,要使得给出的数列在最少的步骤内达到一种排列,怎么做呢?朴素的思考当然是每个数变到距离它最近的一个数,因为这样最划算,但是可能会使某些数变成同一个数。正解是:将给出的数列从小到大排序,为v[1],v[2],...,v[n],那么第i个数v[i]变到i(1<=i<=n)最划算,就像你要把给出的数列尽量向阶梯型数列(1,2,3,...,n)靠拢。这样问题就变得非常简单了,只要排序后扫一遍所有数,答案累加abs(v[i] - i)的值即可。
为什么是正确的呢?同样我们可以尝试改变其中的两种变法,把原先v[i]->i , v[j]->j 改变为:v[i]->j , v[j]->i,不妨设 i < j,即有v[i] < v[j] , 易得:abs(v[i] - j) + abs(v[j] - i) >= abs(v[i] - i) + abs(v[j] - j),得证,所以上述方法是最优解。
1 /************************************************************************* 2 > File Name: c.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Fri 31 Oct 2014 03:26:42 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <stack> 16 #include <queue> 17 #include <iostream> 18 #include <algorithm> 19 using namespace std; 20 #define lp (p << 1) 21 #define rp (p << 1|1) 22 #define getmid(l,r) (l + (r - l) / 2) 23 #define MP(a,b) make_pair(a,b) 24 typedef long long ll; 25 const int INF = 1 << 30; 26 27 int T,n; 28 int a[110]; 29 int sum; 30 31 int main(){ 32 scanf("%d",&T); 33 while(T--){ 34 scanf("%d",&n); 35 for(int i = 1; i <= n; ++i) 36 scanf("%d",a + i); 37 sort(a + 1,a + n + 1); 38 sum = 0; 39 int tmp; 40 for(int i = 1; i <= n; ++i){ 41 sum += abs(a[i] - i); 42 } 43 printf("%d\n",sum); 44 } 45 return 0; 46 }
1006(优乐美的数列)
这题涉及矩阵快速幂,有兴趣的同学可以自学并做一些相关的简单题后再来做这题。
左边的转移矩阵:
1 x^3 3*x^2*y 3*x*y^2 y^3
0 x^3 3*x^2*y 3*x*y^2 y^3
0 x^2 2*x*y y^2 0
0 x y 0 0
0 1 0 0 0
右边的初始矩阵:
S(n-1)
J(n-1)^3
J(n-1)^2 * J(n-2)
J(n-1) * J(n-2)^2
J(n-2)^3
然后就是矩阵乘法和快速幂的模板了,具体过程留给同学们挑战吧!
1 #include <cstdio> 2 #include <cstring> 3 #include <ctime> 4 #include <cstdlib> 5 6 const int N = 5; 7 const int mod = 10007; 8 9 struct Matrix { 10 long long mat[N][N]; 11 }; 12 13 Matrix matrix; 14 Matrix E = {1,0,0,0,0, 15 0,1,0,0,0, 16 0,0,1,0,0, 17 0,0,0,1,0, 18 0,0,0,0,1}; 19 20 Matrix mat_multiply (Matrix &a,Matrix &b) { 21 Matrix temp; 22 for (int i = 0;i < N;i ++) { 23 for (int j = 0;j < N;j ++) { 24 temp.mat[i][j] = 0; 25 for (int k = 0;k < N;k ++) { 26 temp.mat[i][j] += a.mat[i][k] * b.mat[k][j]; 27 } 28 temp.mat[i][j] %= mod; 29 //if (temp.mat[i][j] < 0) 30 // temp.mat[i][j] = (temp.mat[i][j] % mod + mod) % mod; 31 } 32 } 33 return temp; 34 } 35 36 Matrix mat_quickpow (long long n) { 37 Matrix mat = matrix,temp = E; 38 while (n >= 1) { 39 if (n & 1) temp = mat_multiply (temp,mat); 40 n >>= 1; 41 mat = mat_multiply (mat,mat); 42 } 43 return temp; 44 } 45 46 Matrix mat_plus (Matrix &a,Matrix &b) { 47 Matrix temp; 48 for (int i = 0;i < N;i ++) { 49 for (int j = 0;j < N;j ++) { 50 temp.mat[i][j] = (a.mat[i][j] + b.mat[i][j]) % mod; 51 } 52 } 53 return temp; 54 } 55 56 int main () { 57 long long n,x,y; 58 while (~scanf ("%I64d %I64d %I64d",&n,&x,&y)) { 59 memset (matrix.mat,0,sizeof (matrix.mat)); 60 matrix.mat[0][0] = matrix.mat[0][1] = matrix.mat[2][1] = 1; 61 matrix.mat[1][1] = x % mod * x % mod * x % mod; 62 matrix.mat[1][2] = y % mod * y % mod * y % mod; 63 matrix.mat[1][3] = x % mod * x % mod * y % mod * 3; 64 matrix.mat[1][4] = x % mod * y % mod * y % mod * 3; 65 matrix.mat[3][1] = x % mod * x % mod; 66 matrix.mat[3][3] = x % mod * y % mod * 2; 67 matrix.mat[3][4] = y % mod * y % mod; 68 matrix.mat[4][1] = x % mod; 69 matrix.mat[4][3] = y % mod; 70 Matrix trans = mat_quickpow (n); 71 long long ans = trans.mat[0][0] + trans.mat[0][1] + trans.mat[0][2] + trans.mat[0][3] + trans.mat[0][4]; 72 ans %= mod; 73 printf ("%I64d\n",ans); 74 } 75 return 0; 76 }
1007(打土豪,分田地)
本题采用贪心,可以知道要攻克n座房子,你一定会付出n-1次代价,所以最优策略就是使得尽量受到较小的攻击。考虑,人数最多的房子为k,它相邻的房子为a,b,那么我们知道攻克顺序 k,a,b 肯定比攻克顺序 a,k,b 或 b,k,a 的代价更小(只有两座的房子的时候更直观),以此类推,对于一列房子,我们应该先攻克人数最多的房子,再攻克第二多的,第三多的.... 累加得到答案。
1 /************************************************************************* 2 > File Name: ftd.cpp 3 > Author: milaso 4 > Mail: 562058113@qq.com 5 > Created Time: 2014年12月03日 星期三 16时23分37秒 6 ************************************************************************/ 7 8 #include<iostream> 9 #include<math.h> 10 #include<algorithm> 11 #include<string.h> 12 #include<string> 13 #include<stdio.h> 14 #include<fstream> 15 using namespace std; 16 int gj[210]; 17 int vis[210]; 18 19 20 int main(){ 21 int n; 22 while(~scanf("%d",&n)){ 23 gj[0] = 0; 24 int ans =0; 25 memset(vis,0,sizeof(vis)); 26 vis[0]= 1;vis[n+1] = 1; 27 for(int i=1;i<=n;i++) 28 scanf("%d",&gj[i]); 29 for(int i=1;i<=n;i++){ 30 int tmax = 0; 31 for(int j=1;j<=n;j++){ 32 if(!vis[j] && gj[j] > gj[tmax]) 33 tmax = j; 34 } 35 vis[tmax] = 1; 36 if(!vis[tmax - 1]) ans += gj[tmax - 1]; 37 if(!vis[tmax + 1]) ans += gj[tmax + 1]; 38 } 39 printf("%d\n",ans); 40 } 41 }
1008(打土豪,分钻石)
这是一道 dp 题(动态规划) 。不懂的同学可以先学学dp思想。
假设数组 is_r[] 代表里面的第 i 个元素的值代表能否被得到。 dp 的流程如下。
is_r[0] = 1; //首先价值为0是可以达到的
for(int i=0;i<n;i++){
int tep;
scanf("%d",&tep);
for(int j=ans;j>=0;j--) //相当于01背包的循环,逆序更新
if(is_r[j]) //如果发现当前的价值 j 可行
is_r[j+tep] = 1; //更新 j+tep,表示也可行
ans += tep;
}
里面有个坑,就是有有 10000 个宝箱,但是他们的总和只有 1000, 也就是说有值为 0 的宝箱(而且这种值为0的宝箱最多有9000-1个),那么就不需要循环更新,正确代码看下面 :
1 /************************************************************************* 2 > File Name: fzs.cpp 3 > Author: milaso 4 > Mail: 562058113@qq.com 5 > Created Time: 2014 年 12 月 03 日 星期三 18 时 59 分 07 秒 6 ************************************************************************/ 7 #include<iostream> 8 #include<math.h> 9 #include<algorithm> 10 #include<string.h> 11 #include<string> 12 #include<stdio.h> 13 using namespace std; 14 int is_r[2010]; 15 int main(){ 16 int n; 17 while(~scanf("%d",&n)){ 18 memset(is_r,0,sizeof(is_r)); 19 int ans =0; 20 is_r[0] = 1; 21 for(int i=0;i<n;i++){ 22 int tep; 23 scanf("%d",&tep); 24 if(tep == 0) continue; 25 for(int j=ans;j>=0;j--) 26 if(is_r[j]) 27 is_r[j+tep] = 1; 28 ans += tep; 29 } 30 if(ans%2) printf("NO\n"); 31 else if(is_r[ans/2]) printf("YES\n"); 32 else 33 printf("NO\n"); 34 } 35 return 0; 36 }
1009(打土豪,分美女)
技巧题,美女有 100000 个,由于姿色的值是 1-100 ,而时间卡的很死,所以只能用基数排序(俗称小学生排序)。就是在一个 100 的数组里记录每个值出现的次数,然后遍历这个数组,根据总数的一半,得知中间值在哪个数里,再遍历那个原来的数组,找到中间值所在的位置。举个例子:7个数,为1,1,2,3,3,3,4,首先开计数数组num,得到num[1] = 2 , num[2] = 1 , num[3] = 3 , num[4] = 1,其次我们知道中间值是第4个数,那么我们从最小的数开始扫描:num[1](收集2个数),num[2](收集1个数),num[3](收集3个数)注意,当我们扫到第二个数时总收集3个数,扫到第三个数时总收集6个数,刚好>=4,所以我们判定中间值是数3(而且知道是第1个3),那么我们只要再扫一遍数组找出第一个3的位置即可。
(这题姿势不对很容易TLE,sort貌似也卡了?!orz...)。<---(-.- 这题卡成这样我也是服了,顺利成为0A题)
1 /************************************************************************* 2 > File Name: fmn.cpp 3 > Author: milaso 4 > Mail: 562058113@qq.com 5 > Created Time: 2014 年 12 月 03 日 星期三 19 时 24 分 07 秒 6 ************************************************************************/ 7 #include<iostream> 8 #include<math.h> 9 #include<algorithm> 10 #include<string.h> 11 #include<string> 12 #include<stdio.h> 13 #include<fstream> 14 using namespace std; 15 int zs[110],mn[100010]; 16 int main(){ 17 int n; 18 while(~scanf("%d",&n)){ 19 memset(zs,0,sizeof(zs)); 20 for(int i=1;i<=n;i++){ 21 scanf("%d",&mn[i]); 22 zs[mn[i]]++; 23 } 24 int now =0; 25 int ani; 26 for(int i=1;i<=100;i++){ 27 now += zs[i]; 28 if(now >n/2) { 29 ani=i; 30 break; 31 } 32 } 33 for(int i=n;i>0;i--){ 34 if(mn[i] == ani){ 35 if(now == (n+1)/2){ 36 printf("%d\n",i); 37 break; 38 } 39 now--; 40 } 41 } 42 } 43 return 0; 44 }
1010(翔神买钻石)
(嘛,这题是我费了好大力气才出好的,请了许多人来验。>.< 但是没人做有点小伤心,估计是因为时间不足吧...赛后推荐做做,练练暴力)
乍一看没什么思路,然后再看看数据范围,8!没错就是那么小,直接暴力枚举吧骚年!8个钻石,直接枚举哪个是假钻石,再枚举假钻石是更重还是更轻即可,具体细节可以看看代码及其注释!
1 /************************************************************************* 2 > File Name: 1002st2.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Thu 04 Dec 2014 09:06:30 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <stack> 16 #include <queue> 17 #include <iostream> 18 #include <algorithm> 19 using namespace std; 20 #define lp (p << 1) 21 #define rp (p << 1|1) 22 #define getmid(l,r) (l + (r - l) / 2) 23 #define MP(a,b) make_pair(a,b) 24 typedef long long ll; 25 const int INF = 1 << 30; 26 27 int T; 28 int vis[10]; 29 char s1[4][100],s2[4][100],s3[4][100]; 30 31 bool Judge(int p,int flag){ //p表示我们当前设编号为p的钻石为假,flag==-1表示更轻,flag==1表示更重 32 for(int k = 1; k <= 3; ++k){ 33 int len = strlen(s1[k]); 34 if(s3[k][0] == 'b'){ 35 //balance的情况,首先找找两边有没有我们当前枚举的假钻石,如果有肯定矛盾(有假钻石不可能平衡) 36 for(int i = 0; i < len; ++i){ 37 if(s1[k][i] - '0' == p || s2[k][i] - '0' == p) 38 return false; 39 } 40 } 41 else{ //不平衡的情况 42 int pos = 0; 43 //先判断出假钻石的位置,pos == -1表示在左边,pos==1表示在右边 44 for(int i = 0; i < len; ++i){ 45 if(s1[k][i] - '0' == p){ 46 pos = -1; 47 break; 48 } 49 } 50 for(int i = 0; i < len && pos == 0; ++i){ 51 if(s2[k][i] - '0' == p){ 52 pos = 1; 53 break; 54 } 55 } 56 //如果没有假钻石且不平衡,当然也矛盾咯 57 if(pos == 0) return false; 58 //接下来解释判断不平衡的情况和我们枚举的假钻石轻重是否符合了。 59 if(s3[k][0] == 'l'){ 60 if(pos == -1 && flag == -1) return false; 61 if(pos == 1 && flag == 1) return false; 62 } 63 else{ 64 if(pos == -1 && flag == 1) return false; 65 if(pos == 1 && flag == -1) return false; 66 } 67 } 68 } 69 return true; 70 } 71 72 int main(){ 73 scanf("%d",&T); 74 while(T--){ 75 memset(vis,0,sizeof(vis)); 76 for(int i = 1; i <= 3; ++i) 77 scanf("%s%s%s",s1[i],s2[i],s3[i]); 78 int cnt = 0,ans,flag; 79 for(int i = 1; i <= 8; ++i){ //枚举哪个钻石为假 80 if(Judge(i,-1)){ //假设假钻石更轻 81 ++cnt; //cnt用来记录可行的解数 82 ans = i; 83 flag = -1; 84 } 85 if(Judge(i,1)){ //假设假钻石更重 86 ++cnt; 87 ans = i; 88 flag = 1; 89 } 90 } 91 //如果可行解唯一,那么就能判断出假钻石及其轻重了! 92 if(cnt == 1) printf("%d%c\n",ans,flag == -1 ? 'L' : 'H'); 93 else printf("no\n"); 94 } 95 return 0; 96 }
1011(牛B的ACMer)
只要题意看懂就行,直接暴力搜索即可,具体直接看代码吧。(这题许多人没看到这题...有点遗憾啊 >.<)
1 /************************************************************************* 2 > File Name: 1012.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Mon 08 Dec 2014 08:01:39 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <stack> 16 #include <queue> 17 #include <iostream> 18 #include <algorithm> 19 using namespace std; 20 #define lp (p << 1) 21 #define rp (p << 1|1) 22 #define getmid(l,r) (l + (r - l) / 2) 23 #define MP(a,b) make_pair(a,b) 24 typedef long long ll; 25 const int INF = 1 << 30; 26 27 int n,m; 28 int v[110][110]; 29 int tmax[110]; 30 31 int main(){ 32 while(scanf("%d%d",&n,&m) != EOF){ 33 memset(tmax,-1,sizeof(tmax)); 34 for(int i = 1; i <= n; ++i){ 35 for(int j = 1; j <= m; ++j){ 36 scanf("%d",&v[i][j]); 37 tmax[j] = max(tmax[j],v[i][j]); 38 } 39 } 40 int cnt = 0; 41 for(int i = 1; i <= n; ++i){ 42 int flag = 0; 43 for(int j = 1; j <= m; ++j){ 44 if(v[i][j] == tmax[j]){ 45 flag = 1; 46 break; 47 } 48 } 49 cnt += flag; 50 } 51 printf("%d\n",cnt); 52 } 53 return 0; 54 }