首页 > 代码库 > CodeForces Round #286 Div.2

CodeForces Round #286 Div.2

A. Mr. Kitayuta‘s Gift (枚举)

题意:

给一个长度不超过10的串,问能否通过插入一个字符使得新串成为回文串。

分析:

因为所给的串很多,所以可以枚举 “在哪插入” 和 “插入什么”,写一个二重循环枚举新串,判断是否为回文串。时间复杂度为O(n3)

还可只枚举插入位置(在那个位置用一个特殊字符表示),在判断的时候,如果遇到特殊字符,则所插入的字符一定为镜像的字符。

技术分享
 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     else33     {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 (并查集)

题意:

有一个多种颜色的无向图,给出每条边的两个顶点以及该边的颜色。

然后有若干次询问,每次询问两点间共有多少种颜色的通路。

分析:

这道题可以理解为二维的并查集。如果没有颜色的区分,可以判断两点是否连通。

现在给边染上颜色,则我们可以分开处理。最后在询问的时候枚举所有颜色,判断两点是否连通。

技术分享
 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 Edge16 {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=1,最大步长不会超过245 + d,因为技术分享

同样的,如果d≤244,则最小步长为0.

如果d≥245,技术分享

所以,最小步长不会小于max{0, d - 245}.

技术分享
 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 }
代码君

 

CodeForces Round #286 Div.2