AOJ0070 Combination of Number Sequences

問題リンク Combination of Number Sequences

  • 解法

AOJ0008のときにほのめかしたキャッシュを使わないと厳しい問題ですね。
単純に解こうとするとO(n!)です。
9!=3*10^5くらいならば時間的に余裕ですが最大ケースである10!だと厳しいです。
なのでn=10のときはキャッシュを使いました。
数式の後ろ3つの項をキャッシュ対象にしました。
選んだ3つは10桁のビットを使って1つの整数で扱います。選んだ数字が1,2,3ならば2^0+2^1+2^2 = 7、なので7という整数値で表します。
プログラムの先頭で
cach[i][j]:選んだ3つの数字iで和jが作れる数
を求めておきます。
n<10のとき、O(n!)で解き、n=10のときは先頭の7つを決めた後このキャッシュを使えばO(7!)+O(1)で解けます。

  • ソース
import java.util.Scanner;

//Combination of Number Sequences
public class AOJ0070 {

	static int n, s, c;
	static boolean[] used;
	static int[][] cach;
	
	static void dfs(int k, int sum){
		if(s < sum)return;
		if(n==10&&k==7){
			if(s-sum>218)return;
			int id = 0;
			for(int i=0;i<10;i++)if(!used[i])id|=1<<i;
			c+=cach[id][s-sum];
			return;
		}
		if(k==n){
			if(sum==s)c++;
			return;
		}
		for(int i=0;i<10;i++){
			if(!used[i]){
				used[i] = true;
				dfs(k+1, sum+(k+1)*i);
				used[i] = false;
			}
		}
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		cach = new int[1<<10][219];
		for(int i=0;i<10;i++){
			for(int j=0;j<10;j++){
				if(i==j)continue;
				for(int k=0;k<10;k++){
					if(k==i||j==k)continue;
					cach[(1<<i)|(1<<j)|(1<<k)][i*8+j*9+k*10]++;
				}
			}
		}
		while(sc.hasNext()){
			n = sc.nextInt();
			s = sc.nextInt();
			c = 0;
			used = new boolean[10];
			dfs(0,0);
			System.out.println(c);
		}
	}
}