Project Euler Problem 71-80

Project Euler Problem 71-80

Problem 71  Ordered fractions

有序分数

考虑形如n/d的分数,其中n和d均为正整数。如果n < d且其最大公约数为1,则该分数称为最简真分数。
如果我们将d ≤ 8的最简真分数构成的集合按大小升序列出,我们得到:
1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8
可以看出2/5是3/7直接左邻的分数。
将所有d ≤ 1,000,000的最简真分数按大小升序排列,求此时3/7直接左邻的分数的分子。


直接枚举答案就可以
#include
#include

using namespace std;
const double limit=1000000;
double delt=1;
int a,b;
int gcd(int a,int b)
{
    if(!b) return a;
    return gcd(b,a%b);
}

int main()
{
    for(int i=10;i<=limit;i+=1)
        for(int j=(i*3.0/7.0)-3,k=3;k;k--,j+=1)
            if(gcd(i,j)==1&&i*3>j*7&&(3.0/7.0)-j*1.0/double(i)


Problem 72 Counting fractions

分数计数

考虑形如n/d的分数,其中n和d均为正整数。如果n < d且其最大公约数为1,则该分数称为最简真分数。
如果我们将d ≤ 8的最简真分数构成的集合按大小升序列出,我们得到:
1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8
可以看出该集合中共有21个元素。
d ≤ 1,000,000的最简真分数构成的集合中共有多少个元素?

还是暴力解,不过用到了欧拉函数

def euler(n):
	ans=n
	i=2
	while i*i <=n:
		if n%i==0:
			ans=ans*(i-1)/i
			while n%i==0:
				n/=i
		i+=1
	if n<>1:
		ans=ans*(n-1)/n
	return ans

print sum([euler(x) for x in xrange(2,1000001)])
#303963552391L

Problem 73 Counting fractions in a range

分数有范围计数


考虑形如n/d的分数,其中n和d均为正整数。如果n < d且其最大公约数为1,则该分数称为最简真分数。
如果我们将d ≤ 8的最简真分数构成的集合按大小升序列出,我们得到:
1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8
可以看出在1/3和1/2之间有3个分数。
将d ≤ 12,000的最简真分数构成的集合排序后,在1/3和1/2之间有多少个分数?

还是暴力解
#include
using namespace std;

int gcd(int a,int b)
{
    if(!b) return a;
    return gcd(b,a%b);
}
int main()
{
    const int limit=12000;
    int ans=0;
    for(int i=4;i<=limit;i++)
        for(int j=i/3;j<=(i>>1);j++)
            if(3*j>i&&2*j


Problem 74 Digit factorial chains

数字阶乘链



145之所以广为人知,是因为它的各位数字的阶乘之和恰好等于本身:
1! + 4! + 5! = 1 + 24 + 120 = 145
而169则可能不太为人所知,尽管从169开始不断地取各位数字的阶乘之和构成了最长的循环回到169;事实上,只存在三个这样的循环:
169 → 363601 → 1454 → 169
871 → 45361 → 871
872 → 45362 → 872
不难证明,从任意数字出发最终都会陷入循环。例如,
69 → 363600 → 1454 → 169 → 363601 (→ 1454)
78 → 45360 → 871 → 45361 (→ 871)
540 → 145 (→ 145)
从69开始可以得到一个拥有五个不重复项的链,但是从一个小于一百万的数出发能够得到的最长的无重复项链包含有60项。
从小于一百万的数出发,有多少条链恰好包含有60个不重复项?

简单模拟
#include
#include


using namespace std;

int fac[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
set st;
bool have[10000001]={0};
int data[100],num=0,sum,cnt;
/***int check(int n)
{
    st.clear();
    int sum=0;
    int cnt=0;
    while(st.size() >=cnt&&cnt<60)
    {
        st.insert(n);
        while(n)
        {
            sum+=fac[n%10];
            n/=10;
        }
        n=sum;
        sum=0;
        cnt++;
    }
    return cnt;

}***/
int check(int n)
{
    num=sum=cnt=0;
    while(!have[n])
    {
        have[n]=true;
        data[num++]=n;
        while(n)
        {
            sum+=fac[n%10];
            n/=10;
        }
        n=sum;
        sum=0;
        cnt++;
    }
    for(int i=0;i=60)
            ans++;
    cout<= 60:
		return 1
	else:
		return 0
ans=0
for x in xrange(1,1000000):
    ans+=cal(x)

***/

改进版

#include
using namespace std;

const int limit=1000001;
int have[10000006]= {0};
int fac[]= {1,1,2,6,24,120,720,5040,40320,362880};
int sum,cnt,tmp[100];
int cal(int n)
{
    if(have[n]>0) return have[n];
    cnt=0;
    while(!have[n])
    {
        have[n]=-1;
        tmp[cnt++]=n;
        sum=0;
        while(n)
        {
            sum+=fac[n%10];
            n/=10;
        }
        n=sum;
    }
    have[tmp[0]]=have[n]==-1?cnt:cnt+have[n];
    for(int i=1; i=60)
            ans++;
    cout<


Problem 75 Singular integer right triangles

唯一的整数边直角三角形

只能唯一地弯折成整数边直角三角形的电线最短长度是12厘米;当然,还有很多长度的电线都只能唯一地弯折成整数边直角三角形,例如:
12厘米: (3,4,5)
24厘米: (6,8,10)
30厘米: (5,12,13)
36厘米: (9,12,15)
40厘米: (8,15,17)
48厘米: (12,16,20)
相反地,有些长度的电线,比如20厘米,不可能弯折成任何整数边直角三角形,而另一些长度则有多个解;例如,120厘米的电线可以弯折成三个不同的整数边直角三角形。
120厘米: (30,40,50), (20,48,52), (24,45,51)
记电线长度为L,对于L ≤ 1,500,000,有多少种取值只能唯一地弯折成整数边直角三角形?

有一个勾股数定理


#include
#include
#include
using namespace std;
const int limit=1500000;
int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}
int Len[limit+5]= {0};
int main()
{
    int a,b,c,ans=0,sum;
    for(int m=1; m<=1224; m++)
        for(int n=1; nb) swap(a,b);
            if(sum<=limit&&gcd(c,gcd(b,a))==1)
            {
                for(int s=sum; s<=limit; s+=sum)
                    Len[s]++;
            }
        }
    for(int i=1; i<=limit; i++)
        if(Len[i]==1) ans++;
    cout<

Problem 76 Counting summations

加和计数


将5写成整数的和有6种不同的方式:
4 + 1
3 + 2
3 + 1 + 1
2 + 2 + 1
2 + 1 + 1 + 1
1 + 1 + 1 + 1 + 1
将100写成整数的和有多少种不同的方式?

母函数方法来解

#include
#include
#include
using namespace std;
const int cnt=101;
long long mul[cnt]={0};
long long tmp[cnt]={0};

int fact[cnt];
int main()
{
    mul[0]=1;
    for(int i=0;i=cnt)
                    break;
                else
                    tmp[k+j]+=mul[k];
        }
        copy(tmp,tmp+cnt,mul);
    }
    cout<

Problem 77 Prime summations

素数加和


将10写成素数的和有5种不同的方式:

7 + 3
5 + 5
5 + 3 + 2
3 + 3 + 2 + 2
2 + 2 + 2 + 2 + 2

写成素数的和有超过五千种不同的方式的数最小是多少?

还是用母函数(生成函数解)

#include
#include
#include
using namespace std;
int cnt=0;
const int limit=100;
int mul[limit+5]={0};
int tmp[limit+5]={0};
#define mod 1000000

int prime[limit+5];

bool isprime(int n)
{
    for(int i=2;i*i<=n;i++)
        if(n%i==0) return false;
    return true;
}
int main()
{
    mul[0]=1;
    cnt=0;
    for(int i=2;ilimit)
                    break;
                else
                {
                    tmp[k+j]+=mul[k];
                    //tmp[k+j]%=mod;
                }

        }
        copy(tmp,tmp+limit,mul);
    }
    for(int i=2;i=5000)
    {
        cout<

Problem 78 Coin partitions

硬币分拆


记p(n)是将n枚硬币分拆成堆的不同方式数。例如,五枚硬币有7种分拆成堆的不同方式,因此p(5)=7。
OOOOO
OOOO O
OOO OO
OOO O O
OO OO O
OO O O O
O O O O O
找出使p(n)能被一百万整除的最小n值。

还是生成函数,不过我这样写的较慢(要五分钟)

#include
#include
#include
#include
#include
#define mod 1000000
using namespace std;
//product function
const int limit=60000;///55374
int mul[limit+5]= {0}, tmp[limit+5]= {0},cnt=0;
int main()
{
    int start=clock();
    freopen("outp78.txt","w",stdout);
    for(int i=0; i<=limit; i++)
        tmp[i]=mul[i]=1;
    for(int i=2; ilimit)
                    break;
                else
                {
                    tmp[k+j]+=mul[k];
                    tmp[k+j]%=mod;
                }
        copy(tmp,tmp+limit,mul);
        if(mul[i]%mod==0)
        {
            cout<

快点的方法

# list of generalized pentagonal numbers for generating function
k = sum([[i*(3*i - 1)/2, i*(3*i - 1)/2 + i] for i in range(1, 250)], [])
p, sgn, n, m  = [1], [1, 1, -1, -1], 0, 1e6

while p[n]>0:    # expand generating function to calculate p(n)
    n += 1
    px, i = 0, 0
    while k[i] <= n:
        px += p[n - k[i]] * sgn[i%4]
        i += 1
    p.append(px % m)

print "Project Euler 78 Solution =", n

Problem 79 Passcode derivation

密码推断


网上银行常用的一种密保手段是向用户询问密码中的随机三位字符。例如,如果密码是531278,询问第2、3、5位字符,正确的回复应当是317。

在文本文件keylog.txt中包含了50次成功登陆的正确回复。

假设三个字符总是按顺序询问的,分析这个文本文件,给出这个未知长度的密码最短的一种可能。
这题有很多人是手算的,我忘了额
这是论坛里的代码
import time
start = time.time()

f = open('p079_keylog.txt', 'r')  #opens the file

l = [] #initiates a list for the entries

for line in f:  #create the list and remove newline characters
    l.append(line.rstrip('\n'))

a = {}  #initiate a dictionary - will be a dictionary of sets for each integer
k = 0

while k < 10:  #loop from 0-9 excluding 4 and 5
    if k == 4: k = 6
    s1 = set()
    key = k
    for passTry in l:  #for each password attempt list every number that comes after another
        for num in passTry:
            if passTry[0] == str(k):
                s1.add(passTry[1])  #add to sets
                s1.add(passTry[2])
            if passTry[1] == str(k):
                s1.add(passTry[2])
    a[key] = s1
    k += 1


#sort by length of set
for k in sorted(a, key=lambda k: len(a[k]), reverse=True):
   print k,

print "\nExecuted in " + str((time.time()-start)*1000) + " ms"

Problem 80 Square root digital expansion

平方根数字展开


众所周知,如果一个自然数的平方根不是整数,那么就一定是无理数。这样的平方根的小数部分是无限不循环的。
2的平方根为1.41421356237309504880…,它的小数点后一百位数字的和是475。
对于前一百个自然数,求所有无理数平方根小数点后一百位数字的总和。

对于python有大整数这题就很简单了
def cal(n):
	ans=int(n**0.5)
	for x in range(100):
		n*=100
		tmp=ans*10
		while tmp*tmp<=n:
			if tmp*tmp==n:
				return 0
			tmp+=1
		ans=tmp-1
	return sum([int(x) for x in str(ans)[0:100:]])
print sum([cal(x) for x in range(100)])#40886




你可能感兴趣的:(C/C++经典小问题,Project-Euler,Python学习)