递归与递推
递归
[NOIP2002 普及组] 选数
题目描述
已知 n n n 个整数 x 1 , x 2 , ⋯ , x n x_1,x_2,\cdots,x_n x1,x2,⋯,xn,以及 1 1 1 个整数 k k k( k < n k<n k<n)。从 n n n 个整数中任选 k k k 个整数相加,可分别得到一系列的和。例如当 n = 4 n=4 n=4, k = 3 k=3 k=3, 4 4 4 个整数分别为 3 , 7 , 12 , 19 3,7,12,19 3,7,12,19 时,可得全部的组合与它们的和为:
3 + 7 + 12 = 22 3+7+12=22 3+7+12=22
3 + 7 + 19 = 29 3+7+19=29 3+7+19=29
7 + 12 + 19 = 38 7+12+19=38 7+12+19=38
3 + 12 + 19 = 34 3+12+19=34 3+12+19=34
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数: 3 + 7 + 19 = 29 3+7+19=29 3+7+19=29。
输入格式
第一行两个空格隔开的整数 n , k n,k n,k( 1 ≤ n ≤ 20 1 \le n \le 20 1≤n≤20, k < n k<n k<n)。
第二行 n n n 个整数,分别为 x 1 , x 2 , ⋯ , x n x_1,x_2,\cdots,x_n x1,x2,⋯,xn( 1 ≤ x i ≤ 5 × 1 0 6 1 \le x_i \le 5\times 10^6 1≤xi≤5×106)。
输出格式
输出一个整数,表示种类数。
样例 #1
样例输入 #1
4 3
3 7 12 19
样例输出 #1
1
提示
【题目来源】
NOIP 2002 普及组第二题
分析:这题个人认为有点坑,这题开始做的时候,我理解错了,以为里面不能有重复的数字,以为就是一个组合问题加上素数筛,然后就用vector去装,然后去重,再进行判断,然后一直WA两个样例,找不到原因,直到后面去看题解,然后发现,不能去重,重复的也算,:),(下面给出WA的代码)
#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<limits.h>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 25;
LL a[N];
LL b[N];
bool c[N];
vector<LL> res;
LL ans;
int n, k, a_nums;
bool judge_su(int e) {
if(e<=1) return false;
for (int i = 2; i * i <= e; i++) {
if (e % i == 0) {
return false;
}
}
return true;
}
void dfs(int e, int positon) {
if (e == k) {
LL sum = 0;
for (int i = 0; i < k; i++) {
sum += b[i];
// cout << b[i] << ' ';
}
// cout << endl;
// cout<<sum<<endl;
if (judge_su(sum)) {
ans++;
}
return ;
}
for (int i = positon; i < a_nums; i++) {
if (!c[i]) {
c[i] = true;
b[e] = a[i];
dfs(e + 1, i);
c[i] = false;
}
}
}
int main() {
int num;
cin >> n >> k;
memset(c, 0, sizeof c);
for (int i = 0; i < n; i++) {
cin >> num;
res.push_back(num);
}
// sort(res.begin(), res.end());
// res.erase(unique(res.begin(), res.end()), res.end());
a_nums = res.size();
for (int i = 0; i < res.size(); i++) {
a[i] = res[i];
}
// for(int i=0;i<a_nums;i++){
// cout<<a[i]<<' ';
// }
// cout<<endl;
dfs(0, 0);
cout << ans;
return 0;
}
正确代码:(把上面打注释的删了而已,代码可以再简化,懒,过了就行,思路对了就可以)
#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<limits.h>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N = 25;
LL a[N];
LL b[N];
bool c[N];
vector<LL> res;
LL ans;
int n, k, a_nums;
bool judge_su(int e) {
if(e<=1) return false;
for (int i = 2; i * i <= e; i++) {
if (e % i == 0) {
return false;
}
}
return true;
}
void dfs(int e, int positon) {
if (e == k) {
LL sum = 0;
for (int i = 0; i < k; i++) {
sum += b[i];
}
if (judge_su(sum)) {
ans++;
}
return ;
}
for (int i = positon; i < a_nums; i++) {
if (!c[i]) {
c[i] = true;
b[e] = a[i];
dfs(e + 1, i);
c[i] = false;
}
}
}
int main() {
int num;
cin >> n >> k;
memset(c, 0, sizeof c);
for (int i = 0; i < n; i++) {
cin >> num;
res.push_back(num);
}
a_nums = res.size();
for (int i = 0; i < res.size(); i++) {
a[i] = res[i];
}
dfs(0, 0);
cout << ans;
return 0;
}
二分
A-B 数对
** 题目描述**
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!
好吧,题目是这样的:给出一串数以及一个数字 C C C,要求计算出所有 A − B = C A - B = C A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个整数 N , C N, C N,C。
第二行, N N N 个整数,作为要求处理的那串数。
** 输出格式**
一行,表示该串数中包含的满足 A − B = C A - B = C A−B=C 的数对的个数。
样例 #1
样例输入 #1
4 1
1 1 2 3
样例输出 #1
3
提示
对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1≤N≤2000。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1 \leq N \leq 2 \times 10^5 1≤N≤2×105。
保证所有输入数据绝对值小于 2 30 2^{30} 230,且 C ≥ 1 C \ge 1 C≥1。
2017/4/29 新添数据两组
题目分析:这到题有两种做法,但这里我们用二分进行解答,同时这里注意,A-B=C可以转换为A-C=B、数列里面可以有重复的数字,也就是说二分的答案不只一个,所以我们要二分的是这些答案的区间大小,这个大小就是ans答案,二分前提是已经排好序的,我们通过一下步骤来进行解题
- 将数组进行排序
sort(arr,arr+n);
- 枚举每一个数, int B=arr[i]-C;
- 二分查找答案区间的left,也就是第一个B
- 二分查找答案区间的right,也就是最后一个B
- 这里要防止出现 B=arr[i]的情况所以要进行特判一下,如果相同就必须减去1
if(arr[i]==b&&vis){
ans+=r-l;
}else if(arr[i]!=b&&vis){
ans+=r-l+1;
}
完整代码:
#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<limits.h>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=2e5+10;
map<LL,LL> mmap;
LL arr[N];
LL ans;
bool vis=false;
int search_mid1(LL x,int l,int r){
while(l<r){
int mid=(l+r+1)>>1;
if(arr[mid]<=x){
l=mid;
}else{
r=mid-1;
}
}
if(arr[l]==x){
vis=true;
// cout<<arr[l]<<"--"<<l<<endl;
}
return l;
}
int search_mid2(LL x,int l,int r){
while(l<r){
int mid=(l+r)>>1;
if(arr[mid]>=x){
r=mid;
}else{
l=mid+1;
}
}
if(arr[r]==x){
vis=true;
// cout<<arr[r]<<"--"<<r<<endl;
}
return r;
}
int main(){
int n;
LL c;
cin>>n>>c;
for(int i=0;i<n;i++){
cin>>arr[i];
}
sort(arr,arr+n);
// for(int i=0;i<n;i++){
// cout<<arr[i]<<' ';
// }
// cout<<endl;
int l,r;
for(int i=0;i<n;i++){
LL b=arr[i]-c;
vis=false;
r = search_mid1(b,0,n-1);
l = search_mid2(b,0,n-1);
if(arr[i]==b&&vis){
ans+=r-l;
}else if(arr[i]!=b&&vis){
ans+=r-l+1;
}
}
cout<<ans;
return 0;
}
杂题,思路题
求一个数的n进制和最小分式表示
代码:
#include<iostream>
#include<cstdio>
#include<bitset>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<limits.h>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int gcd(int n,int m){
return m==0?n:gcd(m,n%m);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,sum=0;
cin>>n;
for(int i=2;i<n;i++){ //这里可以求出改数i进制的位数和
m=n;
while(m){
sum+=m%i;
m/=i;
}
}
// 最简分式表示,其实就是求分子和分母的最小公约数m,然后 分子/m / 分母/m;
cout<<sum/gcd(sum,n-2)<<"/"<<(n-2)/gcd(sum,n-2)<<endl;
return 0;
}
Trie树
[TJOI2010] 阅读理解
题目描述
英语老师留了 N N N 篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过。
输入格式
第一行为整数 N N N ,表示短文篇数,其中每篇短文只含空格和小写字母。
按下来的 N N N 行,每行描述一篇短文。每行的开头是一个整数 L L L ,表示这篇短文由 L L L 个单词组成。接下来是 L L L 个单词,单词之间用一个空格分隔。
然后为一个整数 M M M ,表示要做几次询问。后面有 M M M 行,每行表示一个要统计的生词。
输出格式
对于每个生词输出一行,统计其在哪几篇短文中出现过,并按从小到大输出短文的序号,序号不应有重复,序号之间用一个空格隔开(注意第一个序号的前面和最后一个序号的后面不应有空格)。如果该单词一直没出现过,则输出一个空行。
样例 #1
样例输入 #1
3
9 you are a good boy ha ha o yeah
13 o my god you like bleach naruto one piece and so do i
11 but i do not think you will get all the points
5
you
i
o
all
naruto
样例输出 #1
1 2 3
2 3
1 2
3
2
提示
对于 30 % 30\% 30% 的数据, 1 ≤ M ≤ 1 0 3 1\le M\le 10^3 1≤M≤103 。
对于 100 % 100\% 100% 的数据, 1 ≤ M ≤ 1 0 4 1\le M\le 10^4 1≤M≤104, 1 ≤ N ≤ 1 0 3 1\le N\le 10^3 1≤N≤103 。
每篇短文长度(含相邻单词之间的空格) ≤ 5 × 1 0 3 \le 5\times 10^3 ≤5×103 字符,每个单词长度 ≤ 20 \le 20 ≤20 字符。
每个测试点时限 2 2 2 秒。
感谢@钟梓俊添加的一组数据。
#include<iostream>
#include<cstdio>
#include<bitset>
#include<map>
#include<set>
#include<vector>
#include<cmath>
#include<limits.h>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=300007;
int son[N][26],idx;
bitset<1001>b[500007]; //这里用bitset来存字符创在哪几层出现过
string str;
int t;
void insert(string s1,int seq){
int p=0;
int lens1=s1.size();
for(int i=0;i<lens1;i++){
int u=s1[i]-'a';
if(!son[p][u]){
son[p][u]=++idx;
}
p=son[p][u];
}
b[p][seq]=1;
}
void query(string s1){
int p=0;
int lens1=s1.size();
for(int i=0;i<lens1;i++){
int u=s1[i]-'a';
if(!son[p][u]){
cout<<' '<<endl;
return;
}
p=son[p][u];
}
bool vis=false;
for(int i=1;i<=t;i++){
if(b[p][i]==1){
if(!vis){
vis=1;
}else{
cout<<' ';
}
cout<<i;
}
}
cout<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int l;
cin>>t;
for(int i=1;i<=t;i++){
cin>>l;
for(int j=1;j<=l;j++){
cin>>str;
insert(str,i);
}
}
cin>>l;
for(int i=1;i<=l;i++){
cin>>str;
query(str);
}
return 0;
}