Boostable

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

    昨天在微博上看到王道论坛说晚上有机试练习赛(http://ac.jobdu.com/contest.php?cid=1053),想来反正也没什么事,何不参加一下。于是晚上吃完饭回到实验室,等着七点开始做题。练习赛结束了,虽然自己全部AC,但感觉时间花的有点长,主要卡在第三道求公约数个数上。看来自己的数论真的弱爆了。现将自己对这四道解题的看法归纳一下,总的来说题目不算很难,前两道超级水,第三道考素数筛选以及因式分解,第四道动态规划。

一、调整方阵

对应题目1470:http://ac.jobdu.com/problem.php?pid=1470

题目描述:

输入一个N(N<=10)阶方阵,按照如下方式调整方阵:
1.将第一列中最大数所在的行与第一行对调。
2.将第二列中从第二行到第N行最大数所在的行与第二行对调。
依此类推...
N-1.将第N-1列中从第N-1行到第N行最大数所在的行与第N-1行对调。
N.输出这个方阵

输入:

包含多组测试数据,每组测试数据第一行为一个整数N,表示方阵的阶数.
接下来输入这个N阶方阵.

输出:

调整后的方阵

样例输入:
4
3 6 8 7
6 7 5 3
8 6 5 3
9 8 7 2
样例输出:
9 8 7 2
6 7 5 3
3 6 8 7
8 6 5 3

水题,就不多说了。代码

 1 #include <stdio.h>
 2 
 3 int data[10][10];
 4 
 5 void adjustment(int i,int n);
 6 int main()
 7 {
 8     int N;
 9     int i,j;
10     while(scanf("%d",&N) != EOF){
11         for(i=0;i<N;++i)
12             for(j=0;j<N;++j)
13                 scanf("%d",&data[i][j]);
14         for(i=0;i<N-1;++i){
15             adjustment(i,N);
16         }
17         for(i=0;i<N;++i){
18             printf("%d",data[i][0]);
19             for(j=1;j<N;++j)
20                 printf(" %d",data[i][j]);
21             printf("\n");
22         }
23     }
24     return 0;
25 }
26  
27 void adjustment(int i,int n)
28 {
29     int max_i = i;
30     int j = i+1;
31     int temp[10];
32     for(;j<n;++j)
33         if(data[j][i] > data[max_i][i])
34             max_i = j;
35     if(max_i != i){
36         for(j=0;j<n;++j){
37             temp[j] = data[i][j];
38             data[i][j] = data[max_i][j];
39             data[max_i][j] = temp[j];
40         }
41     }
42 }

二、货币问题

对应题目1549:http://ac.jobdu.com/problem.php?pid=1549

题目描述:

已知有面值为1元,2元,5元,10元,20元,50元,100元的货币若干(可认为无穷多),需支付价格为x的物品,并需要恰好支付,即没有找零产生。
求,至少需要几张货币才能完成支付。
如,若支付价格为12元的物品,最少需要一张10元和一张2元,即两张货币就可完成支付。

输入:

输入包含多组测试数据,每组仅包含一个整数p(1<=p<=100000000),为需支付的物品价格。

输出:

对于每组输入数据,输出仅一个整数,代表最少需要的货币张数。

样例输入:
10
11
13
样例输出:
1
2
3

也是水题吧,有点贪心的思想吧。先尽可能用最大面值,然后依次考虑较小面值。这题如果限制货币的数量的话,应该就是0-1背包问题吧。

代码

 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     int p;
 6     int i;
 7     int sum;
 8     int money[7] = {1,2,5,10,20,50,100};
 9     while(scanf("%d",&p) != EOF){
10         sum = 0;
11         for(i=6;i>=0;--i){
12             sum += (p/money[i]);
13             p %= money[i];
14         }
15         printf("%d\n",sum);
16     }
17     return 0;
18 }

三、公约数

对应题目1493:http://ac.jobdu.com/problem.php?pid=1493

题目描述:

给定两个正整数a,b(1<=a,b<=100000000),计算他们公约数的个数。
如给定正整数8和16,他们的公约数有:1、2、4、8,所以输出为4。

输入:

输入包含多组测试数据,每组测试数据一行,包含两个整数a,b。

输出:

对于每组测试数据,输出为一个整数,表示a和b的公约数个数。

样例输入:
8 16
22 16
样例输出:
4
2

一直被卡在这道题的超时上。我的做法:采用素数筛选法通过函数computePrime(int n,int maxC)返回第n个素数,其中只需要判断maxC之前的数,注意为了减少空间,我把所有的偶数(当然除了2)都去掉了,所以flag数组只有总数M的一半。在主函数里,首先求出最大公约数,则公约数的个数就等于这个最大公约数的所有约数个数,然后对这个公约数进行因式分解。即假设最大公约数为maxC,则其因式分解为:

\begin{equation*}maxC=p_1^{x_1}\times p_2^{x_2}\times\cdots\times p_m^{x_m}\end{equation*}

所以其公约数个数为: $(x_1+1)\times(x_2+1)\times\cdots\times(x_m+1)$,这个好像叫约数个数定理吧。

代码:

 1 #include <stdio.h>
 2 #include <math.h>
 3 
 4 #define M 100000000
 5 bool flag[M/2];
 6 int prime[6000000];
 7 int primeNum;
 8 
 9 int computePrime(int n,int maxC)
10 {
11     if(n == 1){
12         prime[primeNum++] = 2;
13         return 2;
14     }
15     if(n == 2){
16         prime[primeNum++] = 3;
17         return 3;
18     }
19     int i,j;
20     int pos;
21     i = prime[primeNum-1]+2;
22     while(primeNum < n){
23         pos = ((i-3)>>1);
24         if(!flag[pos]){
25             prime[primeNum++] = i;
26             j = 3*i;
27             while(j<=maxC){
28                 flag[(j-3)>>1] = 1;
29                 j += (i<<1);
30             }
31         }
32         i += 2;
33     }
34     return prime[n-1];
35 }
36 int main()
37 {
38     int a,b;
39     int sum;
40     int maxC,i,j;
41     primeNum = 0;
42     while(scanf("%d%d",&a,&b) != EOF){
43         int t;
44         while(a%b != 0){
45             t = a;
46             a = b;
47             b = t%b;
48         }
49         maxC = b;
50         sum = 1;
51         i = 0;
52         int prime_i;
53         while(maxC != 1){
54             prime_i = computePrime(i+1,maxC);
55             if(maxC % prime_i)
56                 ++i;
57             else{
58                 t=0;
59                 while(maxC%prime_i == 0){
60                     ++t;
61                     maxC /= prime_i;
62                 }
63                 sum *= (t+1);
64                 ++i;
65             }
66         }
67         printf("%d\n",sum);
68     }
69     return 0;
70 }

 四、分糖果

对应题目1550:http://ac.jobdu.com/problem.php?pid=1550

题目描述:

给从左至右排好队的小朋友们分糖果,
要求:
1.每个小朋友都有一个得分,任意两个相邻的小朋友,得分较高的所得的糖果必须大于得分较低的,相等则不作要求。
2.每个小朋友至少获得一个糖果。
求,至少需要的糖果数。

输入:

输入包含多组测试数据,每组测试数据由一个整数n(1<=n<=100000)开头,接下去一行包含n个整数,代表每个小朋友的分数Si(1<=Si<=10000)。

输出:

对于每组测试数据,输出一个整数,代表至少需要的糖果数。

样例输入:
3
1 10 1
3
6 2 3
2
1 1
样例输出:
4
5
2

看完题目,第一反应是要用动态规划解决。其最优子结构为:假如我们已经解决了(s,mid)以及(mid+1,e)这两个子问题,那么我们现在要按如下方法解决子问题(s,e)。首先分三种情况:1)score[mid]=score[mid+1];2)score[mid] > score[mid+1];3)score[mid] < score[mid+1]。

1)由于分数相等的话对糖果的分配不做要求,所以最小的糖果数是两个子问题的和。

2)左边的分数大。此时若左边的糖果多的话,则最小糖果数也是两个子问题的和;否则调整左边的糖果分配,先将[mid]位置的糖果数分配为比[mid+1]位置多一个,然后从mid到s进行调整。

3)右边的分数大。此时若右边的糖果多的话,则最小糖果数也是两个子问题的和;否则调整右边的糖果分配,先将[mid+1]位置的糖果数分配为比[mid]位置多一个,然后从mid+1到e进行调整。

代码

 1 #include<stdio.h>
 2 
 3 int candyNum[100000];
 4 int score[100000];
 5 
 6 int distributeCandy(int,int);
 7 int main()
 8 {
 9     int n,i;
10     while(scanf("%d",&n)!= EOF){
11         for(i=0;i<n;++i){
12             scanf("%d",&score[i]);
13         }
14         printf("%d\n",distributeCandy(0,n-1));
15     }
16     return 0;
17 }
18 
19 int distributeCandy(int s,int e)
20 {
21     if(s == e){
22         candyNum[s] = 1;
23         return 1;
24     }else if(s+1 == e){
25         if(score[s] == score[e]){
26             candyNum[s] = candyNum[e] = 1;
27             return 2;
28         }else if(score[s] > score[e]){
29             candyNum[s] = 2;
30             candyNum[e] = 1;
31             return 3;
32         }else{
33             candyNum[s] = 1;
34             candyNum[e] = 2;
35             return 3;
36         }
37     }else{
38         int total = 0;
39         int mid = (s+e)/2;
40         int left = distributeCandy(s,mid);
41         int right = distributeCandy(mid+1,e);
42         if(score[mid] == score[mid+1]){
43             return left + right;
44         }else if(score[mid] > score[mid+1]){
45             if(candyNum[mid] > candyNum[mid+1])
46                 return left+right;
47             else{
48                 total += (candyNum[mid+1]+1-candyNum[mid]);
49                 candyNum[mid] = candyNum[mid+1] + 1;
50                 int i = mid - 1;
51                 int totalAdd = 0;
52                 for(;i>=s;--i){
53                     if(score[i] <= score[i+1] || (score[i] > score[i+1] && candyNum[i] > candyNum[i+1]))
54                         break;
55                     else{
56                         total += (candyNum[i+1]+1-candyNum[i]);
57                         candyNum[i] = candyNum[i+1] +1;
58                     }
59                 }
60                 return left+right+total;
61             }
62         }else{
63             if(candyNum[mid] < candyNum[mid+1])
64                 return left+right;
65             else{
66                 total += (candyNum[mid]+1-candyNum[mid+1]);
67                 candyNum[mid+1] = candyNum[mid]+1;
68                 int i = mid+2;
69                 for(;i<=e;++i){
70                     if(score[i] <= score[i-1] || (score[i] > score[i-1] && candyNum[i] > candyNum[i-1]))
71                         break;
72                     else{
73                         total += (candyNum[i-1]+1-candyNum[i]);
74                         candyNum[i] = candyNum[i-1]+1;
75                     }
76                 }
77                 return left+right+total;
78             }
79         }
80     }
81 }

PS:此次第一名竟然在十四分钟内四道题全部AC,真是不可思议,估计有些题之前做过,直接拷贝过去或者稍微改一下。

 

 

 

 



posted on 2014-03-03 13:30  Boostable  阅读(682)  评论(2编辑  收藏  举报