トップページに戻る
次の競技プログラミングの問題へ
前の競技プログラミングの問題へ
川添さん問題01 カット・アンド・スクエア
■■■問題■■■
偶数 n に対して、先頭にゼロを持たない n 桁の整数で、次の条件を満たすものを考えましょう。
【条件】
この数を上 n/2 桁と下 n/2 桁とに分け、それぞれを2乗して和をとると、元の数に戻る。
例えば n = 4 の場合は、1233 と 8833 がこの条件を満たします。
12*12 + 33*33 = 1233
88*88 + 33*33 = 8833
なお、下 n/2 桁の数が先頭にゼロを持つときは、余計な先頭のゼロを取り除いて考えて下さい。
(例:020 → 20、0000 → 0)
例えば 9805 に対しては 98*98 + 5*5 という計算を行うことになります。
n=4 の場合にこの条件を満たす数は 1233 と 8833 の二つのみです。これらの総和は 10066 です。
■ 第1問 (Normal)
n=6 の場合にこの条件を満たす数はただ一つ存在します。この数を求めてください。
■ 第2問 (Hard)
n=10 の場合にこの条件を満たす全ての数の総和を求めてください。
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
long[] NArr = { 4, 6, 10 };
foreach (long EachN in NArr) {
List<long> AnswerList = DeriveAnswerList(EachN);
Console.WriteLine("N={0}での解は", EachN);
AnswerList.ForEach(A => Console.WriteLine(A));
Console.WriteLine("合計は{0}", AnswerList.Sum());
Console.WriteLine();
}
Console.WriteLine("経過時間={0}", sw.Elapsed);
}
static List<long> DeriveAnswerList(long pN)
{
var WillReturn = new List<long>();
long UeMin = Derive10Bekijyou(pN / 2 - 1);
long UeMax = Derive10Bekijyou(pN / 2) - 1;
for (long CurrUe = UeMin; CurrUe <= UeMax; CurrUe++) {
long wkC = CurrUe * (CurrUe - UeMax - 1);
List<long> AnswerShitaList = Solve2JiHouteishiki(1, -1, wkC);
AnswerShitaList.RemoveAll(A => A > UeMax);
if (AnswerShitaList.Count > 0) {
AnswerShitaList.ForEach(
A => WillReturn.Add((long)CurrUe * CurrUe + (long)A * A));
}
}
return WillReturn;
}
//10のべき乗を求める
static long Derive10Bekijyou(long pJyousuu)
{
long WillReturn = 1;
for (long I = 1; I <= pJyousuu; I++)
WillReturn *= 10;
return WillReturn;
}
//2次方程式の解を返す
static List<long> Solve2JiHouteishiki(long pA, long pB, long pC)
{
var WillReturn = new List<long>();
long LongHanbetuShiki = pB * pB - 4 * pA * pC;
//実数解を持たない場合
if (LongHanbetuShiki < 0) return WillReturn;
//平方数でない場合
double DoubleHanbetuShiki = (double)LongHanbetuShiki;
long wkSqrt = (long)(Math.Truncate(Math.Sqrt(DoubleHanbetuShiki)));
if (wkSqrt * wkSqrt != LongHanbetuShiki) return WillReturn;
WillReturn.Add((-pB + wkSqrt) / (2 * pA));
WillReturn.Add((-pB - wkSqrt) / (2 * pA));
//負数は不可
WillReturn.RemoveAll(X => X < 0);
//重解ならDistinct
if (LongHanbetuShiki == 0) WillReturn = WillReturn.Distinct().ToList();
return WillReturn;
}
}
C#の実行結果
N=4での解は
1233
8833
合計は10066
N=6での解は
990100
合計は990100
N=10での解は
1765038125
2584043776
7416043776
8235038125
9901009901
合計は29901173703
経過時間=00:00:00.1310222
C++のソース
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <vector>
#include <windows.h>
std::vector<long long> DeriveAnswerVect(long long pN);
long long Derive10Bekijyou(long long pJyousuu);
std::vector<long long> Solve2JiHouteishiki(long long pA,long long pB,long long pC);
void OutDate()
{
SYSTEMTIME stTime;
GetLocalTime(&stTime);
char strTime[100];
wsprintf(strTime , "日時は、%4d年%2d月%2d日%02d時%02d分%02d秒" ,
stTime.wYear , stTime.wMonth , stTime.wDay ,
stTime.wHour , stTime.wMinute , stTime.wSecond);
std::cout << strTime << std::endl;
}
void main()
{
OutDate();
long long NArr[]={4,6,10};
int UB = sizeof(NArr)/sizeof(NArr[0])-1;
for(int I=0;I<=UB;I++){
std::vector<long long> AnswerVect = DeriveAnswerVect(NArr[I]);
printf("N=%lldでの解は\n", NArr[I]);
long long SumVal=0;
for(int J=0;J<=(int)AnswerVect.size()-1;J++){
printf("%lld\n",AnswerVect.at(J));
SumVal+=AnswerVect.at(J);
}
printf("合計は%lld\n",SumVal);
puts("");
}
OutDate();
}
std::vector<long long> DeriveAnswerVect(long long pN)
{
std::vector<long long> WillReturn;
long long UeMin = Derive10Bekijyou(pN / 2 - 1);
long long UeMax = Derive10Bekijyou(pN / 2) - 1;
for (long long CurrUe = UeMin; CurrUe <= UeMax; CurrUe++) {
long long wkC = CurrUe * (CurrUe - UeMax - 1);
std::vector<long long> AnswerShitaVect = Solve2JiHouteishiki(1, -1, wkC);
if ((int)AnswerShitaVect.size() > 0) {
for(int I=0;I<=(int)AnswerShitaVect.size()-1;I++){
if(AnswerShitaVect.at(I) > UeMax) continue;
WillReturn.push_back((long long)CurrUe * CurrUe
+ (long long)AnswerShitaVect.at(I) * AnswerShitaVect.at(I));
}
}
}
return WillReturn;
}
//10のべき乗を求める
long long Derive10Bekijyou(long long pJyousuu)
{
long long WillReturn = 1;
for (long long I = 1; I <= pJyousuu; I++)
WillReturn *= 10;
return WillReturn;
}
//2次方程式の解を返す
std::vector<long long> Solve2JiHouteishiki(long long pA,long long pB,long long pC)
{
std::vector<long long> WillReturn;
long long LongHanbetuShiki = pB * pB - 4 * pA * pC;
//実数解を持たない場合
if (LongHanbetuShiki < 0) return WillReturn;
//平方数でない場合
double DoubleHanbetuShiki = (double)LongHanbetuShiki;
long long wkSqrt = (long long)sqrt(DoubleHanbetuShiki);
if (wkSqrt * wkSqrt != LongHanbetuShiki) return WillReturn;
long long WillAdd1 = (-pB + wkSqrt) / (2 * pA);
long long WillAdd2 = (-pB - wkSqrt) / (2 * pA);
if(WillAdd1 >=0){ //負数は不可
WillReturn.push_back(WillAdd1);
}
if(WillAdd2 >=0){ //負数は不可
if(WillAdd1 != WillAdd2) //重解なら除外
WillReturn.push_back(WillAdd2);
}
return WillReturn;
}
解説
n=4の場合
上2桁をA、下2桁をBとすると
A*A + B*B = A*100 + B を満たすのが必要条件です。
Aをループさせつつ、Bの2次方程式を解いてから、
十分性のチェックとして、Bが実数で99以下であることをチェックしてます。
計算量のオーダーは10の(n/2)乗となります。