diff --git a/solvers/UHotSprings.pas b/solvers/UHotSprings.pas index 601823f..70d0902 100644 --- a/solvers/UHotSprings.pas +++ b/solvers/UHotSprings.pas @@ -22,7 +22,7 @@ unit UHotSprings; interface uses - Classes, SysUtils, Math, Generics.Collections, USolver, UCommon, UMultiIndexEnumerator; + Classes, SysUtils, Math, Generics.Collections, USolver, UCommon, UMultiIndexEnumerator, UBinomialCoefficients; const COperationalChar = '.'; @@ -114,6 +114,7 @@ type TConditionRecord = class private + FBinomialCoefficients: TBinomialCoefficientCache; FValidation: TIntegerList; // List of non-empty, maximum-length parts of the pattern without operational springs ("blocks"). FBlocks: TStringList; @@ -150,11 +151,12 @@ type constref AIndices: TIndexArray; const AStartIndex, AStopIndex: Integer): Int64; function CalcCombinationsBlockAssignedValidations(const ABlockLength: Integer; constref APositionInfos: TValidationPositionInfos; constref AOffsets: TIndexArray; const AStartIndex, AStopIndex: Integer): Int64; + function CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer): Int64; function ParseDamages(const ABlock: string): TDamages; public property Blocks: TStringList read FBlocks; property Validation: TIntegerList read FValidation; - constructor Create; + constructor Create(constref ABinomialCoefficients: TBinomialCoefficientCache); destructor Destroy; override; // Adds all non-empty, maximum-length parts of the pattern without operational springs ("blocks"). procedure AddBlocks(const APattern: string); @@ -164,7 +166,12 @@ type { THotSprings } THotSprings = class(TSolver) + private + // Keeping the binomial coefficients calculator here so it can be shared for all lines. + FBinomialCoefficients: TBinomialCoefficientCache; public + constructor Create; + destructor Destroy; override; procedure ProcessDataLine(const ALine: string); override; procedure Finish; override; function GetDataFileName: string; override; @@ -442,7 +449,7 @@ begin end; /////////////////////////////// - Result := 9999; + Result := 0; // Assigns validation numbers to specific damages. validationToDamageAssignments := TValidationToDamageAssignments.Create(FValidation, FValidationLengths, ADamages, @@ -457,7 +464,7 @@ begin for i := 0 to ADamages.Count - 1 do Write(indices[i] - AStartIndex, ' '); WriteLn(')'); - CalcCombinationsBlockMultiValidations(Length(ABlock), ADamages, indices, AStartIndex, AStopIndex); + Result := Result + CalcCombinationsBlockMultiValidations(Length(ABlock), ADamages, indices, AStartIndex, AStopIndex); end; validationToDamageAssignments.Free; end; @@ -526,9 +533,10 @@ begin WriteLn(' ', position.ValidationIndex, ' ', position.MinStart, ' ', position.MaxStart); WriteLn(' offsets'); + Result := 0; validationPositionOffsets := TValidationPositionOffsets.Create(FValidation, positions); for offsets in validationPositionOffsets do - CalcCombinationsBlockAssignedValidations(ABlockLength, positions, offsets, AStartIndex, AStopIndex); + Result := Result + CalcCombinationsBlockAssignedValidations(ABlockLength, positions, offsets, AStartIndex, AStopIndex); validationPositionOffsets.Free; positions.Free; @@ -537,44 +545,52 @@ end; function TConditionRecord.CalcCombinationsBlockAssignedValidations(const ABlockLength: Integer; constref APositionInfos: TValidationPositionInfos; constref AOffsets: TIndexArray; const AStartIndex, AStopIndex: Integer): Int64; var - i, space, freedom, count: Integer; + i, space: Integer; begin Write(' '); for i in AOffsets do Write(i, ' '); - Write(' count/space/freedom: '); - // TODO: Number of combinations is binom(count + freedom, freedom). - if AStartIndex < APositionInfos[0].ValidationIndex then - begin - count := APositionInfos[0].ValidationIndex - AStartIndex; - space := AOffsets[0] - 2; - freedom := space - FValidationLengths[AStartIndex, APositionInfos[0].ValidationIndex]; - Write(count, '/', space, '/', freedom, ' '); - end - else - Write('X '); - for i := 0 to APositionInfos.Count - 2 do - if APositionInfos[i].ValidationIndex + 1 < APositionInfos[i + 1].ValidationIndex then - begin - count := APositionInfos[i + 1].ValidationIndex - APositionInfos[i].ValidationIndex - 1; - space := AOffsets[i + 1] - AOffsets[i] - FValidation[APositionInfos[i].ValidationIndex] - 2; - freedom := space - FValidationLengths[APositionInfos[i].ValidationIndex + 1, APositionInfos[i + 1].ValidationIndex]; - Write(count, '/', space, '/', freedom, ' '); - end - else - Write('X '); - if APositionInfos.Last.ValidationIndex < AStopIndex then - begin - count := AStopIndex - APositionInfos.Last.ValidationIndex; - space := ABlockLength - AOffsets[APositionInfos.Count - 1] - FValidation[APositionInfos.Last.ValidationIndex]; - freedom := space - FValidationLengths[APositionInfos.Last.ValidationIndex + 1, AStopIndex + 1]; - Write(count, '/', space, '/', freedom, ' '); - end - else - Write('X '); + Write(' count/space/freedoms: '); + space := AOffsets[0] - 2; + Result := CalcCombinationsWildcardSequence(space, AStartIndex, APositionInfos[0].ValidationIndex); + if Result = 0 then begin + WriteLn(' result: ', Result); + Exit; + end; - WriteLn; + for i := 0 to APositionInfos.Count - 2 do begin + space := AOffsets[i + 1] - AOffsets[i] - FValidation[APositionInfos[i].ValidationIndex] - 2; + Result := Result * CalcCombinationsWildcardSequence(space, APositionInfos[i].ValidationIndex + 1, APositionInfos[i + 1].ValidationIndex); + if Result = 0 then begin + WriteLn(' result: ', Result); + Exit; + end; + end; + space := ABlockLength - AOffsets[APositionInfos.Count - 1] - FValidation[APositionInfos.Last.ValidationIndex]; + Result := Result * CalcCombinationsWildcardSequence(space, APositionInfos.Last.ValidationIndex + 1, AStopIndex + 1); + WriteLn(' result: ', Result); +end; + +function TConditionRecord.CalcCombinationsWildcardSequence(const ASequenceLength, AStartIndex, AStopIndex: Integer): + Int64; +var + count, freedoms: Integer; +begin + if AStartIndex < AStopIndex then + begin + count := AStopIndex - AStartIndex; + freedoms := ASequenceLength - FValidationLengths[AStartIndex, AStopIndex]; + Write(count, '/', ASequenceLength, '/', freedoms, ' '); + if freedoms >= 0 then + Result := FBinomialCoefficients.Get(count + freedoms, freedoms) + else + Result := 0; + end + else begin + Result := 1; + Write('X '); + end; end; function TConditionRecord.ParseDamages(const ABlock: string): TDamages; @@ -607,8 +623,10 @@ begin end; end; -constructor TConditionRecord.Create; +constructor TConditionRecord.Create(constref ABinomialCoefficients: TBinomialCoefficientCache); begin + FBinomialCoefficients := ABinomialCoefficients; + FBlocks := TStringList.Create; FValidation := TIntegerList.Create; FDamagesBlocks := TDamagesBlocks.Create; @@ -700,6 +718,17 @@ end; { THotSprings } +constructor THotSprings.Create; +begin + FBinomialCoefficients := TBinomialCoefficientCache.Create; +end; + +destructor THotSprings.Destroy; +begin + FBinomialCoefficients.Free; + inherited Destroy; +end; + procedure THotSprings.ProcessDataLine(const ALine: string); var conditionRecord1, conditionRecord2: TConditionRecord; @@ -710,8 +739,8 @@ begin WriteLn(ALine); WriteLn; - conditionRecord1 := TConditionRecord.Create; - conditionRecord2 := TConditionRecord.Create; + conditionRecord1 := TConditionRecord.Create(FBinomialCoefficients); + conditionRecord2 := TConditionRecord.Create(FBinomialCoefficients); mainSplit := ALine.Split([' ']);