Shiraz University Local Contest 2011 A, UVa-12377 : Number Coding

問題概要

長さL(<20)の数字からなる文字列が与えられる。これらをN個に分割する。分割したとき非減少になっていなくてはならない。これらの数字を9個に割り振る方法が何通りあるか求める問題。

考えたこと

  • 桁数と直前の値を状態としてDP?
  • いやいや、値が小さいんだから全探索で行ける。comb(20, 9)も大した大きさではないし間に合うはず。
  • 分割を全部試した後は単純な組み合わせの問題に落ちるはず。
  • 最初はTLEしてもいいからmap使って正確に書こう。
  • サンプル通った。WA。
  • オーバーフローのことを真面目に考えていなかった。19文字の数字とか出てくるんだし64ビットじゃなきゃダメだろう。
  • まだWA。もしかしてN=0の場合があるのか?
  • さらにWA。もう直すところが思いつかなかったので自棄になってleading zeroのチェックを外したら通った。

本番で通ったコード

計算量O(2^L*L)にlogが付く位?

#include <cstdio>
#include <cstring>
#include <map>
using namespace std;

typedef long long int64;

const int MAX_L = 20;
int N, L;
char str[MAX_L+2];
int64 comb[MAX_L+1][MAX_L+1];
int64 fact[MAX_L+1];

int64 get_comb(const map<int,int>& dict){

	int64 ans = comb[9][N] * fact[N];
	for(map<int,int>::const_iterator itr=dict.begin(); itr!=dict.end(); itr++){
		ans /= fact[itr->second];
	}

	return ans;
}

int64 get_num(int from, int to){
	int64 ret = 0;
	if(0 && str[from] == '0'){
		return -1;
	}
	for(int i=from; i<to; i++){
		ret = 10 * ret + (str[i] & 15);
	}

	return ret;
}

int64 try_it(const int bit){
	if( !((bit>>(L-1))&1) ){
		return 0;
	}
	map<int, int> dict;

	int prev_vert = 0;
	int64 prev_val = 0;
	for(int i=0; i<L; i++)if((bit>>i)&1){
		int64 cur_val = get_num(prev_vert, i+1);
		if(cur_val < prev_val){
			return 0;
		}
		dict[cur_val]++;
		prev_vert = i+1;
		prev_val = cur_val;
	}


	return get_comb(dict);
}

int64 solve(){
	L = strlen(str);
	N = str[0] & 15;
	if(N==0){
		return 1;
	}
	if(N==1){
		return 9;
	}
	for(int i=0; i<L; i++){
		str[i] = str[i+1];
	}
	L--;

	int64 ans = 0;
	for(int bit=0; bit<(1<<L); bit++)if(__builtin_popcount(bit) == N){
		ans += try_it(bit);
	}

	return ans;
}

int main(){

	fact[0] = 1;
	for(int i=0; i<10; i++){
		fact[i+1] = (i+1)*fact[i];
	}

	for(int i=0; i<=MAX_L; i++){
		comb[i][0] = comb[i][i] = 1;
		for(int j=1; j<i; j++){
			comb[i][j] = comb[i-1][j] + comb[i-1][j-1];
		}
	}

	int T;
	scanf("%d ", &T);
	while(T--){
		scanf("%[^\n]%*c", str);
		printf("%lld\n", solve());
	}

	return 0;
}