SRM454 Div1 Medium(500) NumbersAndMatches
動的計画法。a 本のマッチを加えつつ k 本のマッチを動かして残り d 桁の数字から得られる数字の個数を t[d][k][a] とする。数字 x を y に書き換える際に動かすマッチの本数を ux,y、加える(負数で取り除く)マッチの本数を vx,y とする。以下の等式が成り立つ。
t[d][k][a] = Σ0≦i≦9 t[d-1][k-un,i-vn,i][a-vn,i] (vn,i>0)
t[d][k][a] = Σ0≦i≦9 t[d-1][k-un,i][a-vn,i] (vn,i≦0)
ここで、n は N の d 桁目の数字である。場合分けは、ある桁でマッチを取り除き他の桁に加えるという移動を2重に数えないため。
#include <algorithm> using namespace std; class NumbersAndMatches { public: long long differentNumbers( long long N, int K ); long long BT( int digit, long long N, int K, int diff ); }; long long NumbersAndMatches::differentNumbers( long long N, int K ) { // 数字 // ┌4┐ // 0 1 // ├5┤ // 2 3 // └6┘ const int number[10][7] = { { 1, 1, 1, 1, 1, 0, 1 }, // 0 { 0, 1, 0, 1, 0, 0, 1 }, // 1 { 0, 1, 1, 0, 1, 1, 1 }, // 2 { 0, 1, 0, 1, 1, 1, 1 }, // 3 { 1, 1, 0, 1, 0, 1, 0 }, // 4 { 1, 0, 0, 1, 1, 1, 1 }, // 5 { 1, 0, 1, 1, 1, 1, 1 }, // 6 { 0, 1, 0, 1, 1, 0, 0 }, // 7 { 1, 1, 1, 1, 1, 1, 1 }, // 8 { 1, 1, 0, 1, 1, 1, 1 }, // 9 }; // 数字を書き換えるために移動・追加するマッチ数 struct { int move, add; } match[10][10]; for ( int i=0; i<10; i++ ) for ( int j=0; j<10; j++ ) { int d = 0; int a = 0; for ( int k=0; k<7; k++ ) { if ( number[i][k] > number[j][k] ) d++; if ( number[i][k] < number[j][k] ) a++; } match[i][j].move = min( d, a ); match[i][j].add = a - d; } // 桁数 int digit = 0; for ( long long i=1; i<=N; i*=10 ) digit++; // DP表 [d][k][a] // (a-100) 本のマッチを加えながら下 d 桁の k 本のマッチを動かして // 得られる数字の個数 // 桁間で移動するマッチは追加時にカウント static long long table[20][130][200]; memset( table, 0, sizeof table ); table[0][0][100] = 1; for ( int d=1; d<=digit; d++, N/=10 ) for ( int k=0; k<=K; k++ ) for ( int a=0; a<200; a++ ) { for ( int n=0; n<10; n++ ) { int tk = match[N%10][n].move + max( match[N%10][n].add, 0 ); int ta = match[N%10][n].add; if ( 0 <= k-tk && 0 <= a-ta && a-ta < 200 ) table[d][k][a] += table[d-1][k-tk][a-ta]; } } long long ans = 0; for ( int k=0; k<=K; k++ ) ans += table[digit][k][100]; return ans; }