easy:注意consecutive这个词,给同一个人要连续的几段啊。。。。尼玛没有看到啊。。。。然后有了这个词,就可以直接暴力标记那些位置要切。
medium:先把奇数度的点配对,变成个欧拉回路,然后跑一遍,这样不要加的变,每个奇度点才会有1的cost,偶度点都没有,这样显然最小。
hard:长度为25保证总有一个没出现过的字符,这样我们就可以一位一位贪心,每一位靠dp得到上下界(后面没确定的全部设为通配符或者没出现过即全都不匹配,得到上下届,need在上下界这一位就选那个字符),枚举lcs,枚举每一位,枚举每一位选的字母,然后做lcs,总复杂度是n^4*m
(十分感谢safarisoul教会我了n^3*m的做法!她的题解:http://topcoder-algorithm.blogspot.com.au/2014/04/srm517-div1-800-farstrings.html)
#include <vector> #include <list> #include <map> #include <set> #include <deque> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <ctime> #include <cstring> using namespace std; class FarStrings { public: vector<string> find(string); }; int dp[30][30]; bool eq(char a, char b) { if (a == 'A') return true; return a == b; } int lcs(string s, string t) { int i, j, k; int n; memset(dp, 0, sizeof(dp)); n = s.size(); for (i = 0; i <= n; ++i) { dp[i][0] = i; dp[0][i] = i; } for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { if (eq(s[i], t[j])) dp[i + 1][j + 1] = dp[i][j]; else { dp[i + 1][j + 1] = min(dp[i][j + 1] + 1, min(dp[i + 1][j] + 1, dp[i][j] + 1)); } } } return dp[n][n]; } vector<string> FarStrings::find(string t) { int i, j, k; vector<string> ret; string s, p; int n; n = t.size(); for (i = 1; i <= n; ++i) { s.clear(); p.clear(); for (j = 0; j < n; ++j) { s.push_back('A'); p.push_back('B'); } for (j = 0; j < n; ++j) { for (k = 0; k < 26; ++k) { s[j] = k + 'a'; p[j] = k + 'a'; if (i >= lcs(s, t) && i <= lcs(p, t)) break; } } ret.push_back(s); } return ret; }
挖个坑, safarisoul提供了两份资料,貌似能更优?还没来得及看
http://dl.acm.org/citation.cfm?id=214183
http://link.springer.com/chapter/10.1007%2FBFb0030777