题意:用逗号划分数字串,使最后一个子串最小,且开头的子串尽可能大。
方法:
两次DP
1. 求出最后一个子串的值
状态:从前向后DP,dp[i]代表在0~i的子串中,dp[i]~i所代表的子串是最小的最后子串。
转移:枚举dp[i]的值由j-1~0,如果dp[i]~i所代表的子串比dp[i-1]~i-1的子串所代表的值大,则dp[i]~i是最小的最后子串。
2. 求出题目要求的序列
状态:从后向前DP,dp[i]代表在i~len-1的子串中,i~dp[i]所代表的子串是最大的第一个子串。
转移:枚举dp[i]的值由i~len-1,如果i~dp[i]所代表的子串比dp[i]+1~dp[dp[i]+1]的子串大,则可增大dp[i]的值选择更大的第一子串。
以上转自http://blog.csdn.net/cyberzhg/article/details/7538304
/** * Problem:POJ1239 * Author:Shun Yao * Time:2013.5.20 * Result:Accepted * Memo:DP */ #include <cstring> #include <cstdlib> #include <cstdio> using namespace std; const long Maxn = 88; long n, N, minl[Maxn], maxl[Maxn]; char s[Maxn]; long compare(char *s1, char *e1, char *s2, char *e2) { while (*s1 == '0' && s1 != e1) ++s1; while (*s2 == '0' && s2 != e2) ++s2; static long l1, l2; l1 = e1 - s1; l2 = e2 - s2; if (l1 != l2) return l1 - l2; while (s1 != e1) { if (*s1 != *s2) return *s1 - *s2; ++s1; ++s2; } return 0; } int main() { static long i, j; freopen("poj1239.in", "r", stdin); freopen("poj1239.out", "w", stdout); while(scanf(" %s", s), strcmp(s, "0")) { n = strlen(s); memset(minl, 0, sizeof minl); for (i = 1; i < n; ++i) for (j = i - 1; j >= 0; --j) if (compare(s + minl[j], s + j + 1, s + j + 1, s + i + 1) < 0) { minl[i] = j + 1; break; } memset(maxl, 0, sizeof maxl); N = minl[n - 1]; maxl[N] = n - 1; for (i = N - 1; i >= 0 && s[i] == '0'; --i) maxl[i] = n - 1; for (; i >= 0; --i) for (j = N - 1; j >= i; --j) if (compare(s + i, s + j + 1, s + j + 1, s + maxl[j + 1] + 1) < 0) { maxl[i] = j; break; } for (i = 0, j = maxl[i]; i < n; j = maxl[j + 1]) { if (i) putchar(','); while (i < n && i <= j) printf("%c", s[i++]); } putchar('\n'); } fclose(stdin); fclose(stdout); return 0; }