刷题中碰到的那些事

递归与递推

递归

P1036 [NOIP2002 普及组] 选数

[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 1n20 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 1xi5×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;
}

二分

P1102 A-B 数对

A-B 数对

** 题目描述**

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!

好吧,题目是这样的:给出一串数以及一个数字 C C C,要求计算出所有 A − B = C A - B = C AB=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个整数 N , C N, C N,C

第二行, N N N 个整数,作为要求处理的那串数。

** 输出格式**

一行,表示该串数中包含的满足 A − B = C A - B = C AB=C 的数对的个数。

样例 #1

样例输入 #1

4 1
1 1 2 3

样例输出 #1

3

提示

对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1N2000

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1 \leq N \leq 2 \times 10^5 1N2×105

保证所有输入数据绝对值小于 2 30 2^{30} 230,且 C ≥ 1 C \ge 1 C1

2017/4/29 新添数据两组

题目分析:这到题有两种做法,但这里我们用二分进行解答,同时这里注意,A-B=C可以转换为A-C=B、数列里面可以有重复的数字,也就是说二分的答案不只一个,所以我们要二分的是这些答案的区间大小,这个大小就是ans答案,二分前提是已经排好序的,我们通过一下步骤来进行解题

  1. 将数组进行排序
sort(arr,arr+n);
  1. 枚举每一个数, int B=arr[i]-C;
  2. 二分查找答案区间的left,也就是第一个B
  3. 二分查找答案区间的right,也就是最后一个B
  4. 这里要防止出现 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 1M103

对于 100 % 100\% 100% 的数据, 1 ≤ M ≤ 1 0 4 1\le M\le 10^4 1M104 1 ≤ N ≤ 1 0 3 1\le N\le 10^3 1N103

每篇短文长度(含相邻单词之间的空格) ≤ 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值