hdu1286 找新朋友



找新朋友

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6716    Accepted Submission(s): 3476


Problem Description
新年快到了,“猪头帮协会”准备搞一个聚会,已经知道现有会员N人,把会员从1到N编号,其中会长的号码是N号,凡是和会长是老朋友的,那么该会员的号码肯定和N有大于1的公约数,否则都是新朋友,现在会长想知道究竟有几个新朋友?请你编程序帮会长计算出来。
 

Input
第一行是测试数据的组数CN(Case number,1<CN<10000),接着有CN行正整数N(1<n<32768),表示会员人数。
 

Output
对于每一个N,输出一行新朋友的人数,这样共有CN行输出。
 

Sample Input
      
       
       
       
       
2 25608 24027
 

Sample Output
      
       
       
       
       
7680 16016
 

Author
SmallBeer(CML)
 

Source
杭电ACM集训队训练赛(VII)
 



问题如上

在这个题目上耗费了一下午加一晚上的时间。。= =真是略显水。

考虑了很多。。最终正确的代码如下:

#include <iostream>
#include <cstring>
#define max 32768
using namespace std;

int p;
bool num[32768];
short cal[32768];
short prime[32768];
bool current[32768];

int cm(int a, int b);//求公约数并返回最小公约数,b不是素数
void GetPrime();

int main()
{
    int cn, i, no, cur, j;
    GetPrime();
    cin >> cn;
    while(cn --)
    {
        memset(current,1, sizeof(current));
        cin >> cur;
        if(num[cur])
            cal[cur] = cur - 1;
        else
        {
            no = 1;
            for(i = 0; i < p && prime[i] < cur; i++)
            {
                if(cur % prime[i] == 0)
                    for(j = prime[i]; j <= cur; j += prime[i])
                    {
                        current[j] = false;
                    }
            }
            for(i = 2; i <= cur; i++)
            {
                if(current[i])
                    no++;
            }
            cal[cur] = no;
        }
        cout << cal[cur] << endl;
    }
    return 0;
}
void GetPrime()
{
    int i, j, temp;
    memset(num, 1, sizeof(num));

    p = 2;
    prime[0] = 2;
    prime[1] = 3;

    for(i = 1; i <= 3; i++)
        num[i] = true;

    for(i = 4; i <= 32768; i++)
    {
        if(num[i])
        {
            for(j = 0; j < p; j++)
            {
                if(i % prime[j] == 0)
                {
                    num[i] = false;
                    temp = i;
                    while(temp <= 32768)
                    {
                        num[temp] = false;
                        temp += i;
                    }
                    break;
                }
            }
            if(num[i])
                prime[p++] = i;
        }
    }
}


一开始考虑到了各种问题。发现了如下问题:

1.数组下标不正确,与上方不相对应

2.对于sizeof理解不深刻,以及储存空间不够深刻,造成开始的memory溢出;

3.筛法原来就是开一个bool型的数组,然后从里面去除。计算素数的时候使用了筛法。(但是我一直不知道- -);

4.打表的方法可以用于数据量大并且可能有重复的情况。

5.建立全局变量以后里面的值如果并未初始化,存的全部是0.起码int,short,bool型是这个样子。


算法分析:

先求出在该范围的素数,然后求出输入数字的素数因子,使用筛法计算非素数数量。

本题设计到了最小公约数,素数。该题是一道水题,完全可以不计算所有的素数来求出。。但是我还是球了- -。


附加筛法代码,转载自:点击打开链接


//筛选法处理
#include <stdio.h>
#include <string.h>
char hash[32770];                   //使用字符型的减少了内存的开销

int main()
{
    int i,j,t,n,count;
    while(scanf("%d",&t)!=EOF)
    {
        while(t--)
        {
            scanf("%d",&n);
            memset(hash,0,sizeof(hash));  //每次都要赋初始值

            count = 0;
            for(i=2;i<=n;i++)
            {
                if(n%i==0)
                for(j=i;j<=n;j+=i)  // 这里是用了筛选法,注意的是j=i为初始条件 ,而不是j=i+i;
                    hash[j] = 1;
            }
            for(i=1;i<=n;i++)                 
                if(hash[i]==0)
                    count++;
            printf("%d\n",count);
        }
    }
    return 0;
}


另外还有欧拉函数,据说是最快的方法= =转载自 点击打开链接。。另外看打字哇啊什么的似乎是个蒙古汉子(指原作者)

#include <stdio.h>
int ans;
void Eu(int n){
	for(int i = 2; i * i <= n;++i){
		if(!(n%i)){
			ans *= i - 1;
			n /= i;
			while(!(n%i)){
				ans *= i;n/=i; 
			} 
		}
	}
	if(n > 1){
		ans *= n -1;
	}
	return ;
}
int main(){
	int n,test_case;
	scanf("%d",&test_case);
	while(test_case--){
		scanf("%d",&n);
		ans = 1;
		Eu(n);
		printf("%d\n",ans);
	}
	return 0;
}



你可能感兴趣的:(hdu1286 找新朋友)