トップページに戻る
次のC++のサンプルへ
前のC++のサンプルへ
Problem32 2から9までを1回ずつ使う掛け算
問題
7254は面白い性質を持っている. 39 × 186 = 7254と書け, 掛けられる数/掛ける数/積に1から9の数が1回ずつ出現する.
掛けられる数/掛ける数/積に1から9の数が1回ずつ出現するような積の総和を求めよ.
いくつかの積は, 1通り以上の掛けられる数/掛ける数/積の組み合わせを持つが1回だけ数え上げよ.
ソース (正規表現もどきを使う方法)
#include <string>
#include <stack>
#include <vector>
#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <numeric>
bool IsOKPattern(std::string pTarget);
int ExtractKou(int pKouNo,std::string pTarget);
void main()
{
const std::string Kouho = "123456789*=";
std::stack<std::string> stk;
for (int I = 0; I <= (int)Kouho.length() - 1; I++) {
if (Kouho.at(I) != '*' && Kouho.at(I) != '=') {
stk.push(Kouho.substr(I,1));
}
}
std::vector<int> AnsVect;
while (stk.empty() == false) {
std::string Popped = stk.top(); stk.pop();
if(Popped.length() == Kouho.length()) {
if(IsOKPattern(Popped)==false) continue;
int kou1 = ExtractKou(1,Popped);
int kou2 = ExtractKou(2,Popped);
int kou3 = ExtractKou(3,Popped);
if (kou1 < kou2 && kou1 * kou2 == kou3) {
if(std::find(AnsVect.begin(),AnsVect.end(),kou3)==AnsVect.end()){
AnsVect.push_back(kou3);
std::cout << Popped.c_str() << std::endl;
}
}
continue;
}
for (int I = 0; I <= (int)Kouho.length() - 1; I++) {
if (Popped.find(Kouho.at(I)) != std::string::npos) continue;
std::string WillPush = Popped + Kouho.at(I);
if (WillPush.find("=*") != std::string::npos) continue;
if (WillPush.find("*=") != std::string::npos) continue;
if (WillPush.find("=") != std::string::npos
&& WillPush.find("*") == std::string::npos) continue;
// =がない状態で5桁の数値があったら枝切り
if (WillPush.find("=") == std::string::npos){
bool WillEdakiri = false; int NumCnt = 0;
for(int I=0;I<= (int)WillPush.length()-1;I++){
if('1' <= WillPush.at(I) && WillPush.at(I) <= '9') NumCnt++;
else NumCnt=0;
if(NumCnt == 5) WillEdakiri=true;
}
if(WillEdakiri) continue;
}
stk.push(WillPush);
}
}
printf("合計=%d\n",std::accumulate(AnsVect.begin(),AnsVect.end(),0));
}
//文字列がマッチパターン^[1-9]+\*[1-9]+=[1-9]+$を満たすかを返す
bool IsOKPattern(std::string pTarget)
{
int Jyoutai=0;
//状態1 ^[1-9]+
//状態2 \*
//状態3 [1-9]+
//状態4 =
//状態5 [1-9]+$
for(int I=0;I<= (int)pTarget.length()-1;I++){
if('1' <= pTarget.at(I) && pTarget.at(I) <= '9'){
if (Jyoutai == 0){
Jyoutai=1;
continue;
}
else if (Jyoutai == 1 || Jyoutai == 3 || Jyoutai == 5)
continue;
else if (Jyoutai == 2 || Jyoutai == 4){
Jyoutai++;
continue;
}
}
else if (pTarget.at(I) == '*'){
if (Jyoutai == 1){
Jyoutai++;
continue;
}
return false;
}
else if (pTarget.at(I) == '='){
if (Jyoutai == 3){
Jyoutai++;
continue;
}
return false;
}
}
return Jyoutai==5;
}
//^[1-9]+\*[1-9]+=[1-9]+$ のpKouNo番目の[1-9]+を数値に変換して返す
int ExtractKou(int pKouNo,std::string pTarget)
{
std::string CapturedStr;
bool IsCapture = (pKouNo==1);
for(int I=0;I<= (int)pTarget.length()-1;I++){
if('1' <= pTarget.at(I) && pTarget.at(I) <= '9'){
if(IsCapture) CapturedStr += pTarget.at(I);
}
else if (pTarget.at(I) == '*'){
IsCapture = (pKouNo==2);
}
else if (pTarget.at(I) == '='){
IsCapture = (pKouNo==3);
}
}
return atoi(CapturedStr.c_str());
}
実行結果 (正規表現もどきを使う方法)
4*1963=7852
4*1738=6952
48*159=7632
42*138=5796
39*186=7254
28*157=4396
27*198=5346
合計=45228
ソース (正規表現もどきを使わない方法)
#include <vector>
#include <valarray>
#include <algorithm>
#include <stdio.h>
#include <numeric>
bool IsKouho(int pTargetInt);
bool IsOK(std::valarray<int> &pTargetArr);
void main()
{
std::vector<int> KouhoVect;
for (int I = 1; I <= 9999; I++) {
if (IsKouho(I)) KouhoVect.push_back(I);
}
std::vector<int> ProdValVect;
for (int I = 1; I <= (int)KouhoVect.size() - 1; I++) {
for (int J = I + 1; J <= (int)KouhoVect.size() - 1; J++) {
int ProdVal = KouhoVect.at(I) * KouhoVect.at(J);
if(ProdVal >= 9876543) break; //積の値で枝切り
if(std::find(ProdValVect.begin(),ProdValVect.end(),ProdVal) != ProdValVect.end())
continue;
std::valarray<int> WKArr(3);
WKArr[0] = KouhoVect.at(I); WKArr[1] = KouhoVect.at(J); WKArr[2] = ProdVal;
if(IsOK(WKArr)) {
ProdValVect.push_back(ProdVal);
printf("%d * %d = %d\n",KouhoVect.at(I),KouhoVect.at(J),ProdVal);
}
}
}
printf("合計=%d\n",std::accumulate(ProdValVect.begin(),ProdValVect.end(),0));
}
bool IsKouho(int pTargetInt)
{
std::valarray<bool> CheckNumArr(false,10);
do {
int wk = pTargetInt % 10;
if (wk == 0) return false;
if (CheckNumArr[wk]) return false;
CheckNumArr[wk] = true;
pTargetInt /= 10;
} while (pTargetInt > 0);
return true;
}
bool IsOK(std::valarray<int> &pTargetArr)
{
std::valarray<bool> CheckNumArr(false,10);
for(int I=0;I<=(int)pTargetArr.size()-1;I++){
int CopiedInt = pTargetArr[I];
do {
int wk = CopiedInt % 10;
if (wk == 0) return false;
if (CheckNumArr[wk]) return false;
CheckNumArr[wk] = true;
CopiedInt /= 10;
} while (CopiedInt > 0);
}
for (int I = 1; I <= 9; I++)
if (CheckNumArr[I] == false) return false;
return true;
}
実行結果 (正規表現もどきを使わない方法)
4 * 1738 = 6952
4 * 1963 = 7852
12 * 483 = 5796
18 * 297 = 5346
28 * 157 = 4396
39 * 186 = 7254
48 * 159 = 7632
合計=45228
解説
正規表現もどきを使う方法では、深さ優先探索で組み合わせを列挙しつつ、枝切りも行ってます。
ですが、正規表現もどきを使わない方法のほうが、圧倒的に高速ですね。