H106OJ | 第二次练习(查找)

---题目:幸运数

Description

幸运数是波兰数学家乌拉姆命名的。它采用与生成素数类似的“筛法”生成。

首先从1开始写出自然数1,2,3,4,5,6,....

1 就是第一个幸运数。

我们从2这个数开始。把所有序号能被2整除的项删除,变为:

1 _ 3 _ 5 _ 7 _ 9 ....

把它们缩紧,重新记序,为:

1 3 5 7 9 .... 。这时,3为第2个幸运数,然后把所有能被3整除的序号位置的数删去。注意,是序号位置,不是那个数本身能否被3整除!! 删除的应该是5,11, 17, ...

此时7为第3个幸运数,然后再删去序号位置能被7整除的(19,39,...)

最后剩下的序列类似:

1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, ...

Input

输入两个正整数m n, 用空格分开 (m < n < 1000*1000)

Output

程序输出 位于m和n之间的幸运数的个数(不包含m和n)。

Sample Input 1 

1 20

Sample Output 1

5

Hint

HINT:时间限制:1.0s 内存限制:256.0MB

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 +10;
const int inf = 0x3f3f3f3f;
int a[maxn];
int n, m;
void dfs(int num)
{
    if(a[num] > n)
        return;
    int tmp = num;
    for(int i = num; i < n; ++i)
    {
        if(i % a[num])
            a[tmp++] = a[i];
    }
    dfs(num + 1);
}
int main()
{
    cin >> m >> n;
    for(int i = 1; i <= n; ++i)
        a[i] = i * 2 - 1;
    dfs(2);
    int i = 1, ans = 0;
    while(a[i] < n)
    {
        if(a[i] > m)
            ++ans;
        ++i;
    }
    cout << ans << endl;
    return 0;
}

---题目:子集选取

Description

一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1000000007。

Input

输入一行两个整数N,K

Output

输出一个整数表示答案。

Sample Input 1 

3 2

Sample Output 1

6

Hint

HINT:时间限制:1.0s 内存限制:256.0MB

1 <= K <= N <= 10 ^ 6。

AC代码:

#include<iostream>
#include<cstdio>
#define N 1000009
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll inv[N],jie[N],ni[N],n,k,ans;
inline ll power(ll x,ll y){
    ll ans=1;
    while(y){
        if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1;
    }
    return ans;
}
inline ll C(int n,int m){
    return jie[n]*ni[m]%mod*ni[n-m]%mod;
}
int main(){
    cin>>n>>k;
    inv[0]=1;
    for(int i=1;i<=n;++i)inv[i]=inv[i-1]*2%(mod-1);
    jie[0]=1;
    for(int i=1;i<=n;++i)jie[i]=jie[i-1]*i%mod;ni[n]=power(jie[n],mod-2);
    for(int i=n-1;i>=0;--i)ni[i]=ni[i+1]*(i+1)%mod;
    for(int i=k;i<=n;++i){
         if((i-k)&1)ans-=C(n-k,i-k)*(power(2,inv[n-i])-1)%mod;
         else ans+=C(n-k,i-k)*(power(2,inv[n-i])-1)%mod;
         ans=(ans%mod+mod)%mod;
    }
    ans=ans*C(n,k)%mod;
    cout<<ans;
    return 0;
}

---题目:P1003

Description

作为一名网络警察,你的任务是监视电子邮件,看其中是否有一些敏感的关键词。不过,有些狡猾的犯罪嫌疑人会改变某些单词的字母顺序,以逃避检查。请编写一个程序,发现这种调整过顺序的关键词。程序的输入有两行,第一行是关键词列表,第二行是待检查的句子。程序的输出为在该句子中所找到的经过顺序调整的关键词。(单词全部为小写,单词之间以一个空格分隔,每一行的单词个数不限)

Input

第一行是关键词列表,第二行是待检查的句子。

Output

经过顺序调整的关键词。(单词全部为小写,单词之间以一个空格分隔,每一行的单词个数不限)

Sample Input 1 

guns mines missiles
aameric ssell snug dan iimsssle ot sit neeemis

Sample Output 1

guns missiles

Hint

HINT:时间限制:1.0s 内存限制:256.0MB

AC代码:

#include<stdio.h>
#include<string.h>
int num1,num2;
char str1[1000][500],str2[1000][500];
char a1[1000][500],a2[1000][500];
int word(char str[],char arr[][500])
{
	int k=0,j=0;
	char *p=str;
	while(*p)
	{
		if(*p==' ' && *(p+1)!=' ')
		{
			arr[k][j]='\0';
			k++;
			j=0;
		}
		else
		{
			arr[k][j++]=*p;
		}
		p++; 
	}
	return k;
}
void strSort(char arr[][500],int num,char a[][500])
{
	int i,j,k,len;
	char temp;
	for(i=0;i<=num;i++)
	{
		strcpy(a[i],arr[i]);
	}
	for(i=0;i<=num;i++)
	{
		len=strlen(a[i])-1;
		for(j=0;j<len;j++)
		{
			for(k=j+1;k<=len;k++)
			{
				if(a[i][j]>a[i][k])
				{
					temp=a[i][j];
					a[i][j]=a[i][k];
					a[i][k]=temp;
				}
			}
		}
	}
}
void cmp(char str[][500])
{
	int i,j;
	for(i=0;i<=num1;i++)
	{
		for(j=0;j<=num2;j++)
		{
			if(strcmp(a1[i],a2[j])==0)
			{
				printf("%s ",str1[i]);
				break;
			}
		}
	}
}
int main()
{
	char arr1[5000];
	char arr2[5000]; 
	gets(arr1);
	gets(arr2);
	num1=word(arr1,str1);
	num2=word(arr2,str2);
	strSort(str1,num1,a1);
	strSort(str2,num2,a2);
	cmp(a2);
	return 0;
}

开始时看到这道题一头雾水,字母排列顺序不同该怎么进行匹配?后来突然意识到可以将关键词和待检查的句子中的单词先按照ASCII码表中字母的排列顺序重新排列,匹配查找结束后再以关键词列表中的排列方式输出待检查句子中含有的关键词。

---题目:找素数

Description

给定区间[L, R] , 请计算区间中素数的个数。

Input

两个数L和R。

Output

一行,区间中素数的个数。

Sample Input 1 

2 11

Sample Output 1

5

Hint

HINT:时间限制:1.0s 内存限制:256.0MB

2 <= L <= R <= 2147483647 R-L <= 1000000

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=9999999;
int sum=0;
bool is_prime[MAXN], vis[MAXN];
void getprime(ll a, ll b)
{
    memset(vis, true, sizeof(vis));
    for(int i = 0; i < b - a+1; i++)
        is_prime[i] = true;
    for(int i = 2; (ll)i * i <=b; i++)
	{
        if(vis[i])
		{
            for(int j = 2*i; (ll)j*j <=b; j += i)
				vis[j] = false;
 
            for(ll j=max(2LL,(a+i-1)/i) *i; j <=b; j += i)
			{
                is_prime[j-a]=false;
            }
        }
    }
}
int main()
{
    ll a, b;
    ios::sync_with_stdio(false);
    cin>>a>>b;
    getprime(a,b);
    for(ll i = a; i <= b; i++)
    {
        if(is_prime[i-a])
        sum++;
    }
    cout<<sum<<endl;
    return 0;
}

---题目:凶手

Description

巴斯维克命案抓住了六个嫌疑犯,他们的口供如下:

A:我不是罪犯

B:A、C中有一个是罪犯

C:A和B说了假话

D:C和F说了假话

E:其他五个人中,只有A和D说了真话

F:我是罪犯

他们中只有一半说了真话,凶手只有一个。

本题可能有多种可能性,即正确答案(找到唯一的凶手)可能有多个,但每一个可能的答案(某一个是凶手)都满足上述口供。

请编程找出可能的凶手输出。

样例:(假设唯一的凶手是A或者D或者E,则输出结果为三行,按字母顺序依次输出)

A

D

E

Input

如题所示。

Output

如题所示。

Sample Input 1 

参考上文 

Sample Output 1

参考上文

Hint

HINT:时间限制:1.0s 内存限制:256.0MB

AC代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int N = 6;
int x[N+1];

bool A(){
    if (!x[0]) return 1;
    return 0;
}

bool B(){
    if (x[0]||x[2]) return 1;
    return 0;
}

bool C(){
    if (!A()&&!B()) return 1;
    return 0;
}

bool F(){
    if (x[5]) return 1;
    return 0;
}

bool D(){
    if (!C()&&!F()) return 1;
    return 0;
}

bool E(){
    if (A()&&D()&&!B()&&!C()&&!F()) return 1;
    return 0;
}

int main(){
    for (int i=0;i<6;i++){
        memset(x,0,sizeof(x));
        x[i]=1;
        if (A()+B()+C()+D()+E()+F()==3) printf ("%c\n",'A'+i);
    }
    return 0;
}

这道题重要的是理清楚思路,首先假设A是凶手,然后一个一个判断他们所说话的真(1)假(0);然后假设B,以此类推......最后得到:假设X时,所有六人所说的话为真(即1)的个数 >= 3,那么该X即为凶手。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值