「オイラープロジェクト151~160」の編集履歴(バックアップ)一覧はこちら
「オイラープロジェクト151~160」(2012/11/24 (土) 13:15:24) の最新版変更点
追加された行は緑色になります。
削除された行は赤色になります。
*問い151
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%20151
封筒の中身の期待値を求める問題。
シンプルに動的計画法で期待値を求めるだけです。
丁寧ですがコードサイズが膨らみました。
この問題簡単すぎますが他の人のコードを読んでると楽しい問題です。
問題をどんな構造で解くか。
色々なとらえ方があって楽しいですね。
#include<stdio.h>
#include<map>
#include<string.h>
#include<iostream>
#include<time.h>
struct E{
__int64 paper[6];
bool operator<(const E& e)const{
for(int i=1;i<=5;i++){
if(paper[i]!=e.paper[i])return paper[i]<e.paper[i];
}
return false;
}
void print(){
std::cout<<"(";
for(int i=1;i<=5;i++){
std::cout<<paper[i]<<" ";
}
std::cout<<")";
}
};
int main(){
double start=clock();
double ans=0,perm,p;
std::map<E,double> memo,next;
std::map<E,double>::iterator it;
E e,es[6];
memset(e.paper,0,sizeof(e.paper));
for(int i=1;i<=5;i++){
es[i]=e;
es[i].paper[i]=1;
}
e.paper[1]=1;
memo[e]=1.0;
for(int i=1;i<=14;i++){
for(it=memo.begin();it!=memo.end();it++){
double paperCount=0;
e=(*it).first;
for(int j=1;j<=5;j++){
paperCount+=e.paper[j];
}
if(paperCount==0)continue;
for(int j=1;j<=5;j++){
e=(*it).first;
if(e.paper[j]<1)continue;
e.paper[j]--;
for(int k=j+1;k<=5;k++){
e.paper[k]++;
}
if(next.find(e)==next.end())next[e]=0;
next[e]+=(*it).second*(e.paper[j]+1.0)/paperCount;
}
}
memo.clear();
memo.insert(next.begin(),next.end());
next.clear();
for(int i=1;i<=5;i++){
ans+=memo[es[i]];
}
std::cout<<i+1<<" "<<ans<<" "<<memo.size()<<"\n";
}
std::cout<<"ans="<<ans<<" time="<<clock()-start;
}
他の人のコードを参考にショートコードを作ってみました。
私はショートコードに向いてないなと痛感した次第です。
#include<stdio.h>
#include<string.h>
#include<iostream>
double memo[2][131072]={{0,1.0,0},{0}};
int main(){
double ans=0,all;
int slide[]={0,1,3,6,11},mask[]={1,3,7,31,63},add[]={2121,2118,2104,1984,-2048};
for(int i=1;i<=14;i++){
int now=(i+1)&1,next=i&1;
memset(memo[next],0,sizeof(memo[next]));
for(int j=0;j<131072;j++){
all=0;
for(int size=0;size<5;size++)all+=(j>>slide[size])&mask[size];
for(int size=0;size<5;size++){
if(((j>>slide[size])&mask[size])!=0){
int p=j+add[size];
if(p>=131072||p<0)break;
memo[next][p]+=memo[now][j]*((j>>slide[size])&mask[size])/all;
}
}
}
ans+=memo[next][2]+memo[next][8]+memo[next][64]+memo[next][2048];
}
std::cout<<"ans="<<ans;
}
*問い151
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%20151
封筒の中身の期待値を求める問題。
シンプルに動的計画法で期待値を求めるだけです。
丁寧ですがコードサイズが膨らみました。
この問題簡単すぎますが他の人のコードを読んでると楽しい問題です。
問題をどんな構造で解くか。
色々なとらえ方があって楽しいですね。
#include<stdio.h>
#include<map>
#include<string.h>
#include<iostream>
#include<time.h>
struct E{
__int64 paper[6];
bool operator<(const E& e)const{
for(int i=1;i<=5;i++){
if(paper[i]!=e.paper[i])return paper[i]<e.paper[i];
}
return false;
}
void print(){
std::cout<<"(";
for(int i=1;i<=5;i++){
std::cout<<paper[i]<<" ";
}
std::cout<<")";
}
};
int main(){
double start=clock();
double ans=0,perm,p;
std::map<E,double> memo,next;
std::map<E,double>::iterator it;
E e,es[6];
memset(e.paper,0,sizeof(e.paper));
for(int i=1;i<=5;i++){
es[i]=e;
es[i].paper[i]=1;
}
e.paper[1]=1;
memo[e]=1.0;
for(int i=1;i<=14;i++){
for(it=memo.begin();it!=memo.end();it++){
double paperCount=0;
e=(*it).first;
for(int j=1;j<=5;j++){
paperCount+=e.paper[j];
}
if(paperCount==0)continue;
for(int j=1;j<=5;j++){
e=(*it).first;
if(e.paper[j]<1)continue;
e.paper[j]--;
for(int k=j+1;k<=5;k++){
e.paper[k]++;
}
if(next.find(e)==next.end())next[e]=0;
next[e]+=(*it).second*(e.paper[j]+1.0)/paperCount;
}
}
memo.clear();
memo.insert(next.begin(),next.end());
next.clear();
for(int i=1;i<=5;i++){
ans+=memo[es[i]];
}
std::cout<<i+1<<" "<<ans<<" "<<memo.size()<<"\n";
}
std::cout<<"ans="<<ans<<" time="<<clock()-start;
}
他の人のコードを参考にショートコードを作ってみました。
私はショートコードに向いてないなと痛感した次第です。
#include<stdio.h>
#include<string.h>
#include<iostream>
double memo[2][131072]={{0,1.0,0},{0}};
int main(){
double ans=0,all;
int slide[]={0,1,3,6,11},mask[]={1,3,7,31,63},add[]={2121,2118,2104,1984,-2048};
for(int i=1;i<=14;i++){
int now=(i+1)&1,next=i&1;
memset(memo[next],0,sizeof(memo[next]));
for(int j=0;j<131072;j++){
all=0;
for(int size=0;size<5;size++)all+=(j>>slide[size])&mask[size];
for(int size=0;size<5;size++){
if(((j>>slide[size])&mask[size])!=0){
int p=j+add[size];
if(p>=131072||p<0)break;
memo[next][p]+=memo[now][j]*((j>>slide[size])&mask[size])/all;
}
}
}
ans+=memo[next][2]+memo[next][8]+memo[next][64]+memo[next][2048];
}
std::cout<<"ans="<<ans;
}
*問い154
(x+y+z)^20万乗を展開した時係数が10^12乗の倍数になる計数の数を数える問題。
解法
2分で解けるコードを組めました。
20万段目の組み合わせだけを計算します。
20万段目の一番外の列を計算すれば20万段目の残りの列を求めることは簡単です
この時、2と5の倍数が12個以上含まれる数だけが10^12となります。
なので2と5の素因数だけを数えれば問題が解けます。
#include<stdio.h>
#include<iostream>
#include<time.h>
const int up=20*10000;
int intFacts2[up+2]={0},intFacts5[up+2]={0};
int row2[up+2]={0},row5[up+2]={0};
int memo2[up+2]={0},memo5[up+2]={0};
int factCount(int n,int fact){
if(n<1)return 0;
int count=0;
while(n%fact==0){
n/=fact;
count++;
}
return count;
}
void setIntFacts2And5(){
for(int i=2;i<=up;i++){
int n=i;
intFacts2[i]=factCount(n,2);
intFacts5[i]=factCount(n,5);
}
}
int main(){
double start=clock();
setIntFacts2And5();
__int64 ans=0;
for(int i=1;i<up;i++){
row2[i]=(row2[i-1]+intFacts2[up-i+1]-intFacts2[i]);
row5[i]=(row5[i-1]+intFacts5[up-i+1]-intFacts5[i]);
}
for(int i=1;i<=up;i++){
for(int j=1;j<=i+1;j++){
memo2[j]=memo2[j-1]+intFacts2[i-j+1]-intFacts2[j];
memo5[j]=memo5[j-1]+intFacts5[i-j+1]-intFacts5[j];
ans+=(memo2[j-1]+row2[i]>=12&&memo5[j-1]+row5[i]>=12);
}
}
std::cout<<"\nans="<<ans<<"\ntime="<<clock()-start;
}