A. Mr. Kitayuta's Gift (枚举)
题意:
给一个长度不超过10的串,问能否通过插入一个字符使得新串成为回文串。
分析:
因为所给的串很多,所以可以枚举 “在哪插入” 和 “插入什么”,写一个二重循环枚举新串,判断是否为回文串。时间复杂度为O(n3)
还可只枚举插入位置(在那个位置用一个特殊字符表示),在判断的时候,如果遇到特殊字符,则所插入的字符一定为镜像的字符。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <cstring> 3 4 char s[15], s1[15]; 5 int l; 6 7 bool judge() 8 { 9 for(int i = 0; i < (l+1)/2; ++i) 10 if(s1[i] == 0) s1[i] = s1[l-i]; 11 else if(s1[l-i] == 0) s1[l-i] = s[i]; 12 else if(s1[i] != s1[l-i]) return false; 13 14 return true; 15 } 16 17 int main() 18 { 19 scanf("%s", s); 20 l = strlen(s); 21 bool flag = false; 22 for(int pos = 0; pos <= l; ++pos) 23 {//枚举所加入字符的位置,s1为得到的新串 24 int i; 25 for(i = 0; i < pos; ++i) s1[i] = s[i]; 26 s1[i++] = 0; 27 for(; i <= l; ++i)s1[i] = s[i-1]; 28 if(judge()) { flag = true; break; } 29 } 30 31 if(!flag) puts("NA"); 32 else 33 { 34 for(int i = 0; i <= l; ++i) 35 if(s1[i] == 0) printf("a"); 36 else printf("%c", s1[i]); 37 } 38 39 return 0; 40 }
B. Mr. Kitayuta's Colorful Graph (并查集)
题意:
有一个多种颜色的无向图,给出每条边的两个顶点以及该边的颜色。
然后有若干次询问,每次询问两点间共有多少种颜色的通路。
分析:
这道题可以理解为二维的并查集。如果没有颜色的区分,可以判断两点是否连通。
现在给边染上颜色,则我们可以分开处理。最后在询问的时候枚举所有颜色,判断两点是否连通。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <vector> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxn = 100 + 10; 8 9 int G[maxn][maxn]; 10 int n, m; 11 int p[maxn][maxn]; 12 //找颜色为color的边中,x的父节点 13 int findp(int color, int x) { return x == p[color][x] ? x : p[color][x] = findp(color, p[color][x]); } 14 15 struct Edge 16 { 17 int u, v; 18 Edge(int u=0, int v=0):u(u), v(v) {} 19 }; 20 21 vector<Edge> edges[maxn]; 22 23 int main() 24 { 25 //freopen("in.txt", "r", stdin); 26 int n, m, Mc = 0; 27 scanf("%d%d", &n, &m); 28 //并查集初始化 29 for(int i = 1; i <= m; ++i) 30 for(int j = 1; j <= n; ++j) 31 p[i][j] = j; 32 33 for(int i = 0; i < m; ++i) 34 { 35 int a, b, c; 36 scanf("%d%d%d", &a, &b, &c); 37 Mc = max(Mc, c); 38 int pa = findp(c, a); 39 int pb = findp(c, b); 40 if(pa != pb) p[c][pa] = pb; 41 } 42 43 int Q; 44 scanf("%d", &Q); 45 for(int i = 0; i < Q; ++i) 46 { 47 int u, v, cnt = 0; 48 scanf("%d%d", &u, &v); 49 for(int c = 1; c <= Mc; ++c) //枚举所有颜色 50 if(findp(c, u) == findp(c, v)) 51 cnt++; 52 printf("%d\n", cnt); 53 } 54 55 return 0; 56 }
C. Mr. Kitayuta, the Treasure Hunter (DP)
题意:
参见原文吧,=_=||
Mr. Kitayuta, the Treasure Hunter
分析:
设dp(i, j)表示跳到第i个岛步长为j,wi表示第i个岛的宝石数,最大能取到的宝石数。
状态转移方程为 dp(i, j) = wi + max{dp(i+j-1, j-1), dp(i+j, j), dp(i+j+1, j+1)}
上式是从后往前推的,最终答案是dp(d, d)。
从前往后推也是一样的,不过要维护一个最大值。
还有一个很严重的问题就是:30000×30000的空间太大了!
再深入分析,其实步长的变化范围没有3w那么大。
假设每一步步长为前面步长加一,可以求出最大步长。类似地,求出最小步长。
同样的,如果d≤244,则最小步长为0.
所以,最小步长不会小于max{0, d - 245}.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 5 int max3(int a, int b, int c) 6 { return max(max(a, b), c); } 7 8 int w[70000], dp[65000][500]; 9 10 int main() 11 { 12 //freopen("in.txt", "r", stdin); 13 14 int n, d; 15 scanf("%d%d", &n, &d); 16 for(int i = 0; i < n; ++i) 17 { 18 int x; 19 scanf("%d", &x); 20 w[x]++; 21 } 22 23 int maxp, minp = 1; 24 for(int n = 0; ; n++) if(d*(n+1)+n*(n+1)/2 >= 30000) 25 { 26 maxp = d + n; //最大步长 27 break; 28 } 29 for(int n = 0; d >= 245; n++) if(d*(n+1)-n*(n+1)/2 >= 30000) 30 { 31 minp = d - n; //最小步长 32 break; 33 } 34 35 for(int x = 30000; x >= d; --x) 36 for(int l = minp; l <= maxp; ++l) 37 dp[x][l-minp+1] = w[x] + max3(dp[x+l][l-minp+1], dp[x+l+1][l+2-minp], dp[x+l-1][l-minp]); 38 39 printf("%d\n", dp[d][d+1-minp]); 40 41 return 0; 42 }