POJ 2292 - Optimal Keypad
http://poj.org/problem?id=2292
概要
与えられた M 個の単語を入力するのにボタンを押す回数が最小になるように,携帯電話のキーを割り当てたい.
そのために,a, b, ..., z, +, *, /, ? という30個の文字が書かれたテープを考え,これを12個に最適に分割する問題を考える. ここから11個の文字を選び,それらの文字の直前でテープを切り,それぞれが各ボタンに左から順に割り当てられる.
最適に切ったとき,その11個の文字を答える.複数の答えがある場合には辞書順で最小のものを答える.
制約
- 1 <= M <= 10^4
- 単語の長さ <= 30
解法
dp[i][k] = k 個めの区切りを i 番目の文字にしたときの最小コスト
という DP 表を埋めた後,経路復元する.
poj/2292.cc1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 int main() 6 { 7 static int c2i[128]; 8 static char i2c[30]; 9 for (int i = 0; i < 26; i++) { 10 c2i['a'+i] = i; 11 i2c[i] = 'a'+i; 12 } 13 c2i['+'] = 26; 14 i2c[26] = '+'; 15 c2i['*'] = 27; 16 i2c[27] = '*'; 17 c2i['/'] = 28; 18 i2c[28] = '/'; 19 c2i['?'] = 29; 20 i2c[29] = '?'; 21 22 ios::sync_with_stdio(false); 23 int T; 24 cin >> T; 25 while (T-- > 0) { 26 int M; 27 cin >> M; 28 vector<int> cnt(30, 0); 29 for (int i = 0; i < M; i++) { 30 string s; 31 cin >> s; 32 for (string::const_iterator it = s.begin(); it != s.end(); ++it) { 33 ++cnt[c2i[int(*it)]]; 34 } 35 } 36 37 static const int INF = 10000000; 38 vector<vector<int> > dp(30, vector<int>(12, INF)); 39 vector<vector<int> > dp_prev(30, vector<int>(12, -1)); 40 dp[0][0] = 0; 41 42 for (int k = 1; k < 12; k++) { 43 for (int i = 0; i < 30; i++) { 44 for (int j = 0; j < i; j++) { 45 int s = 0; 46 for (int x = j; x < i; x++) { 47 s += cnt[x]*(x-j); 48 } 49 if (dp[j][k-1]+s < dp[i][k]) { 50 dp[i][k] = dp[j][k-1]+s; 51 dp_prev[i][k] = j; 52 } 53 } 54 } 55 } 56 int mincost = INF; 57 int idx = -1; 58 for (int i = 0; i < 30; i++) { 59 int s = 0; 60 for (int j = i; j < 30; j++) { 61 s += cnt[j] * (j-i); 62 } 63 if (dp[i][11]+s < mincost) { 64 mincost = dp[i][11]+s; 65 idx = i; 66 } 67 } 68 char ans[12]; 69 ans[11] = '\0'; 70 for (int k = 11; k > 0; k--) { 71 ans[k-1] = i2c[idx]; 72 idx = dp_prev[idx][k]; 73 } 74 cout << ans << endl; 75 } 76 return 0; 77 }