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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

   

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

posted @ 2014-12-11 02:25  Naturain  阅读(350)  评论(0编辑  收藏  举报