- 众数问题
Description
给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。多重集S中重数最大的元素称为众数。例如,S={1,2,2,2,3,5}。多重集S的众数是2,其重数为3。对于给定的由n 个自然数组成的多重集S,计算S的众数及其重数。如果出现多个众数,请输出最小的那个。
Input
输入数据的第1行是多重集S中元素个数n(n<1300000);接下来的n行中,每行有一个最多含有5位数字的自然数,。
Output
输出数据的第1行给出众数,第2行是重数。
Sample
Input
6 1 2 2 2 3 5
Output
2
3
首先对数组排序,有序数组相同的数连在一起。之后二分法查找重数数目,二分边界i,j意味着a[mid]这个数的数量,用cnt记录。
如果递归边界l,r小于当前认为的众数数量cnt,说明这个边界里没有数量比它多的,直接返回。
快排优化记一下
#include <bits/stdc++.h> using namespace std; int a[1300100]; int ans = 0, cnt = 0; void erfen(int l, int r){ if(r - l + 1 < cnt) return; int mid = (l+r) >> 1; int i = mid, j = mid; while(a[mid] == a[i]) i--; while(a[mid] == a[j]) j++; int len = j - i - 1; if(cnt < len || (cnt == len && ans > a[mid])){ ans = a[mid]; cnt = len; } erfen(l, i); erfen(j, r); } int change(int l, int r){ int t = rand() % (r - l + 1) + l; int temp = a[l]; a[l] = a[t]; a[t] = temp; t = a[l]; int i = l, j = r; while(i < j){ while(a[j] >= t && i < j) j--; a[i] = a[j]; while(a[i] <= t && i < j) i++; a[j] = a[i]; } a[i] = t; return i; } void quickSort(int l, int r){ if(l < r){ int t = change(l, r); quickSort(l, t-1); quickSort(t+1, r); } } int main() { int n; scanf("%d", &n); for(int i = 0; i<n; i++){ scanf("%d", &a[i]); } quickSort(0, n-1); erfen(0, n-1); printf("%d %d ", ans, cnt); return 0; }
B - 整数因子分解问题
Description
12=12;
12=6*2;
12=4*3;
12=3*4;
12=3*2*2;
12=2*6;
12=2*3*2;
12=2*2*3。
对于给定的正整数n,计算n共有多少种不同的分解式。
Input
Output
Sample
Input
12
Output
8
看完题...嗯...想不出来,看完别人的:原来是用这种方法解决的吗...
K本身代表一个分解方法,sum从1开始,如果k能再分解,次数继续增加。
诡异的出错点在于...按照题目要求它的最大值好像没有那么大...但用int就会WA
#include <bits/stdc++.h> using namespace std; int a[500000]; int n; long long int searchs(int k){ long long int sum = 1; if((k < 500000) && a[k] != 0){ return a[k]; } for(int i = 2; i<=sqrt(k); i++){ if(k % i == 0){ sum += searchs(i); if(i * i != k){ sum += searchs(k / i); } } } if(k < 500000) a[k] = sum; return sum; } int main() { scanf("%d", &n); printf("%lld ", searchs(n)); return 0; }
C - 顺序表应用7:最大子段和之分治递归法
Description
给定n(1<=n<=50000)个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n。 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。
注意:本题目要求用分治递归法求解,除了需要输出最大子段和的值之外,还需要输出求得该结果所需的递归调用总次数。
递归调用总次数的获得,可以参考以下求菲波那切数列的代码段中全局变量count的用法:
#include
int count=0;
int main()
{
int n,m;
int fib(int n);
scanf("%d",&n);
m=fib(n);
printf("%d %d
",m,count);
return 0;
}
int fib(int n)
{
int s;
count++;
if((n==1)||(n==0)) return 1;
else s=fib(n-1)+fib(n-2);
return s;
}
Input
第一行输入整数n(1<=n<=50000),表示整数序列中的数据元素个数;
第二行依次输入n个整数,对应顺序表中存放的每个数据元素值。
Output
一行输出两个整数,之间以空格间隔输出:
第一个整数为所求的最大子段和;
第二个整数为用分治递归法求解最大子段和时,递归函数被调用的总次数。
Sample
Input
6 -2 11 -4 13 -5 -2
Output
20 11
Hint
感觉动态规划做这题比分治要好理解多了...但是不行,还要递归次数
这个二分看了好一会为什么要这么整。分段之后得不到两段共同拥有的中间部分的加和,所以还得手动求
比如样例里这个,最大和子段应该是11+13-4=20,左段为(-2,11,-4),右段为(13,-5,-2),左段求得11,右端求得13,从中间值mid开始向左和向右求和,找出连接左右段的最大和(-4+11)+(13) = 20;
#include <bits/stdc++.h> using namespace std; int a[50010]; int ans = 0, cnt = 0; int erfen(int l, int r){ cnt++; //统计重数 if(l == r){ if(a[l] > 0) return a[l]; else return 0; } int mid = (l + r) >> 1; int s1 = 0, s2 = 0, s = 0; for(int i = mid; i >= l; i--){ s += a[i]; if (s > s1) s1 = s; } s = 0; for(int i = mid+1; i <= r; i++){ s += a[i]; if (s > s2) s2 = s; } int maxl = erfen(l, mid); int maxr = erfen(mid+1, r); maxl = max(maxl, maxr); if(maxl > s1 + s2) return maxl; else return s1 + s2; } int main() { int n; scanf("%d", &n); for(int i = 0; i<n; i++){ scanf("%d", &a[i]); } ans = erfen(0, n-1); printf("%d %d ", ans, cnt); return 0; }
D - 骨牌铺方格
Description
在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出铺放方案的总数. 例如n=3时,为2× 3方格,骨牌的铺放方案有三种,如下图:
Input
输入包含一个整数n,表示该测试实例的长方形方格的规格是2×n (0< n<=50)。
Output
输出铺放方案的总数。
Sample
Input
3
Output
3
跟走楼梯一样,当你想要使用n个骨牌时,你可以从之前只用了n-1个骨牌的方块中补上一块,也可以从之前的缺两块的方格中补上两块。(拼一块和两块的方法数目已知)
不过这也是分治吗...?
#include <bits/stdc++.h> using namespace std; long long int a[55]; int main() { int n; scanf("%d", &n); a[1] = 1; a[2] = 2; for(int i = 3; i<=n; i++){ a[i] = a[i-1] + a[i-2]; } printf("%lld ", a[n]); return 0; }
#include <bits/stdc++.h> using namespace std; int a[50010]; int ans = 0, cnt = 0; int erfen(int l, int r){ cnt++; //统计重数 if(l == r){ if(a[l] > 0) return a[l]; else return 0; } int mid = (l + r) >> 1; int s1 = 0, s2 = 0, s = 0; for(int i = mid; i >= l; i--){ s += a[i]; if (s > s1) s1 = s; } s = 0; for(int i = mid+1; i <= r; i++){ s += a[i]; if (s > s2) s2 = s; } int maxl = erfen(l, mid); int maxr = erfen(mid+1, r); maxl = max(maxl, maxr); if(maxl > s1 + s2) return maxl; else return s1 + s2; } int main() { int n; scanf("%d", &n); for(int i = 0; i<n; i++){ scanf("%d", &a[i]); } ans = erfen(0, n-1); printf("%d %d ", ans, cnt); return 0; }