Tävlingsprogrammering/Uppgifter/Olikheter

Från Wikibooks


Olikheter

På hur många sätt kan följande olikhet satisfieras?

där A1, A2, B, C, D, E1, E2 alla är givna heltal mellan 1 och 1000 och ? ska ersättas med heltal. Svaret ryms alltid i ett 64-bitars heltal.

Exempel 1

A1 ? 14
A2 ? 5
B ? 1
C ? 9
D ? 7
E1 ? 10
E2 ? 3

Svar

Antal lösningar: 3

Förklaring: Här är lösningarna utskrivna:


Exempel 2

A1 ? 1
A2 ? 5
B ? 4
C ? 3
D ? 2
E1 ? 5
E2 ? 1

Svar

Antal lösningar: 369

Exempel 3

A1 ? 900
A2 ? 950
B ? 900
C ? 950
D ? 980
E1 ? 950
E2 ? 900

Svar

Antal lösningar: 177631

Lösning[redigera]

Här gäller det att känna till det elfte budordet "Du skall icke jämföra flyttal", samt hur man bör jämföra bråk. D.v.s:

förutsatt att samtliga tal är positiva.

Kalla de okända talen för b, c och d.

Vilket genererar följande kodsnutt.

long sum = 0;

for(int b = A1*B/A2 + 1; b*E2<B*E1; b++)
for(int c = b*C/B + 1; c*E2<C*E1; c++)
for(int d = c*D/C + 1; d*E2<D*E1; d++)
sum++;

Man inser ganska snabbt att den kan förenklas till följande:

long sum = 0;

int tod = D*E1/E2;
//Om divisionen går jämnt ut måste termen justeras.
if(D*E1%E2==0) tod--;

for(int b = A1*B/A2 + 1; b*E2<B*E1; b++)
for(int c = b*C/B + 1; c*E2<C*E1; c++)
sum += tod - c*D/C;

Här skulle man kunna tro att man kan ersätta den inre loopen med någon form av aritmetisk summa, men det kommer inte ge samma resultat då avrundningen kommer att bli annorlunda. Men vi kan konstatera att vi beräknar stora delar av den inte loopen flera gånger om och om igen. Så varför komplicera till det? Vi kör lite dynamisk programmering och beräknar summan från respektive värde mellan 0 och C*E1/E2 upp till C*E1/E2.

Slutligen landar vi i följande program.

import java.util.*;

public class Olikheter
{
	public static void main(String [] klein)
	{
		Scanner scan = new Scanner(System.in);

		System.out.print("A1 ? ");
		int A1 = scan.nextInt();

		System.out.print("A2 ? ");
		int A2 = scan.nextInt();

		System.out.print("B ? ");
		int B = scan.nextInt();

		System.out.print("C ? ");
		int C = scan.nextInt();

		System.out.print("D ? ");
		int D = scan.nextInt();

		System.out.print("E1 ? ");
		int E1 = scan.nextInt();

		System.out.print("E2 ? ");
		int E2 = scan.nextInt();

		long sum = 0;

		final int tod = D*E1%E2==0 ? D*E1/E2-1 : D*E1/E2;
		final int toc = C*E1%E2==0 ? C*E1/E2-1 : C*E1/E2;

		long [] cp = new long [toc+2];

		for(int c = toc; c>=0; c--) cp[c] = cp[c+1] + tod - c*D/C;

		for(int b = A1*B/A2 + 1; b*E2<B*E1; b++)
		{
			sum += cp[b*C/B + 1];
		}

		System.out.println("\nAntal lösningar: " + sum);
	}
}


En elegant lösning i Haskell (av Anton Fors Hurdén):

l a b c = quot (a * b) c + 1

u a b c = q - if r == 0 then 1 else 0
	where (q, r) = quotRem (a * b) c

main = do
	a1 <- readLn
	a2 <- readLn
	b <- readLn
	c <- readLn
	d <- readLn
	e1 <- readLn
	e2 <- readLn
	print $ sum [1 | x <- [l a1 b a2..u e1 b e2], y <- [l x c b..u e1 c e2], z <- [l y d c..u e1 d e2]]