思路1:
每次贪心地选择满足i * (i - 1) / 2 <= k最大的i并从k中减去i * (i - 1) / 2,直至k为0。由于函数x * (x - 1) / 2的增长速度比2x要慢,所以这种方法的收敛速度比每次减掉某个2的幂还要快一些,26个小写字母是肯定够用的。
实现:
1 #include <bits/stdc++.h> 2 using namespace std; 3 int main() 4 { 5 int k; 6 cin >> k; 7 for (char c = 'a'; c <= 'z'; c++) 8 { 9 int i = 1; 10 while (i * (i - 1) / 2 <= k) i++; 11 k -= (i - 1) * (i - 2) / 2; 12 for (int j = 1; j < i; j++) cout << c; 13 } 14 return 0; 15 }
思路2:
完全背包+恢复路径。
与贪心方法相比,这种方法可以找到使用最少字母种类的方案。
注意恢复路径的方法。
实现:
1 #include <bits/stdc++.h> 2 using namespace std; 3 int a[1005], inv[100005], ans[1005], dp[100005], path[100005]; 4 int main() 5 { 6 int k; 7 cin >> k; 8 if (!k) { cout << "a"; return 0; } 9 int i = 1; 10 memset(dp, 0x3f, sizeof dp); 11 memset(path, 0, sizeof path); 12 dp[0] = 0; path[0] = -1; 13 for ( ; i * (i - 1) <= 2 * k; i++) 14 { 15 a[i] = i * (i - 1) >> 1; 16 inv[i * (i - 1) >> 1] = i; 17 } 18 for (int j = 2; j < i; j++) 19 { 20 for (int p = a[j]; p <= k; p++) 21 { 22 if (dp[p - a[j]] + 1 < dp[p]) 23 { 24 dp[p] = dp[p - a[j]] + 1; 25 path[p] = p - a[j]; 26 } 27 } 28 } 29 while (path[k] != -1) { ans[inv[k - path[k]]]++; k = path[k]; } 30 char now = 'a'; 31 for (int j = 2; j < i; j++) 32 { 33 if (ans[j]) 34 { 35 for (int x = 0; x < ans[j]; x++) 36 { 37 for (int p = 0; p < j; p++) cout << now; 38 now++; 39 } 40 } 41 } 42 return 0; 43 }