标签:
A,B,C水
D。
有一个串,长度为n+2,
现在知道他的所有n个 长度为3的子串是什么
求出原始的串
这题跟POJ 2337有点像
最后抽象出的问题就是求欧拉通路:
将每个长度为3的子串, 前两个字母(数字)看成一个结点, 后两个字母(数字)看成一个结点,
然后这个子串就相当于 一条从前一个结点到后一个结点的边
欧拉通路的要求就是所有边都要走一遍, 所以最后就是求欧拉通路了
结点的个数最大为62*62,边的数量显然就是n了
因为两个字母(数字)hash成数字的话也就是62*62种
首先要判断这个欧拉通路是否存在
首先计算每个结点的入度和出度,
然后如果一个有向图存在欧拉通路。
则任意结点的出度跟入度的差的绝对值 不大于1
并且统计abs(出度-入度)==1 的结点的数量,假设为x个
则x必然为0或者2
如果为0个,则欧拉通路在任意一点都可当做起点
如果为2个,则欧拉通路从(出度-入度)==1 的结点出发
求欧拉通路的时候我们dfs即可,每条边会有一个编号,保证每条边只走一次。
但是需要注意的是,这个n是有20万的, 重边和自环多了会出一些问题。
所以我就将重边看成1条,但是对其计数,在dfs的时候用边的数量当做是否访问过的标记
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <cmath> #include <algorithm> #include <map> #include <ctime> #define MAXN 111111 #define MAXM 1122222 #define INF 1000000007 #define eps 1e-8 using namespace std; int n; typedef pair<int, int> PII; vector<PII> g[66 * 66]; int num[66 * 66]; int in[66 * 66], out[66 * 66]; int fa[66 * 66], v[66 * 66]; int en[66 * 66][66 * 66]; int vis[222222], nv[222222]; int st[222222]; char s[211111][6]; int find(int x) { if(x == fa[x]) return x; int t = find(fa[x]); fa[x] = t; return t; } void join(int x, int y) { int fx = find(x); int fy = find(y); if(fx != fy) fa[fx] = fy; } int getc(char c) { if(c >= 'a' && c <= 'z') return c - 'a'; if(c >= 'A' && c <= 'Z') return c - 'A' + 26; return c - '0' + 52; } int ind; void dfs(int u, int eid) { for(int i = 0; i < g[u].size(); i++) { int tid = g[u][i].second; if(vis[tid]) { vis[tid] --; dfs(g[u][i].first, tid); } } if(eid) st[ind++] = eid; } int main() { scanf("%d", &n); for(int i = 0; i < 66 * 66; i++) fa[i] = i; for(int i = 1; i <= n; i++) { scanf("%s", s[i]); int a = getc(s[i][0]); int b = getc(s[i][1]); int c = getc(s[i][2]); int id1 = a * 62 + b; int id2 = b * 62 + c; v[id1] = 1; v[id2] = 1; join(id1, id2); if(en[id1][id2] == 0) en[id1][id2] = i, g[id1].push_back(PII(id2, i)); in[id2]++; out[id1]++; vis[en[id1][id2]]++; } int tmp = -1, flag = 0; for(int i = 0; i < 66 * 66; i++) { if(!v[i]) continue; if(tmp == -1) tmp = find(i); else { if(tmp != find(i)) { flag = 1; break; } } } int cnt = 0, src = -1; for(int i = 0; i < 66 * 66; i++) { if(!v[i]) continue; if(abs(in[i] - out[i]) >= 2) { flag = 1; break; } else if(abs(in[i] - out[i]) == 1) { cnt ++; if(out[i] - in[i] == 1) src = i; } } if(src == -1) src = tmp; if(flag) { printf("NO\n"); } else if(cnt == 0 || cnt == 2) { dfs(src, 0); printf("YES\n"); for(int i = ind - 1; i >= 0; i--) { int tid = st[i]; if(i == ind - 1) printf("%s", s[tid]); else printf("%c", s[tid][2]); } printf("\n"); } else { printf("NO\n"); } return 0; }
题意是
给出n个区间。
然后第i个区间表示的是第i个左括号与右括号的 下标距离范围
求出一个合法的括号序列 并且满足这些范围
然后后来想了想,貌似是个记忆化dp, 而且并不难。。
令dp[i][j]表示第i个到第j个左括号是否都成功的匹配到了右括号
那么在dfs的时候,对于dp[i][j] , 我们枚举i被第几个右括号匹配了,显然是从第i个右括号枚举到第j个右括号,假设被第k个右括号匹配并且满足之前给的范围了
然后就可以去寻找其子状态 dp[i + 1][k] 以及dp[k+1][j]
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <cmath> #include <algorithm> #include <map> #include <ctime> #define MAXN 111111 #define MAXM 1122222 #define INF 1000000007 #define eps 1e-8 using namespace std; int dp[666][666]; int l[666], r[666]; char s[3333]; int n; int p[3333]; int dfs(int L, int R) { if(L > R) return 1; if(dp[L][R]) return dp[L][R]; for(int i = L; i <= R; i++) { if(i - L + 1 >= l[L] && i - L + 1 <= r[L] ) { if(dfs(L + 1, i) == 1 && dfs(i + 1, R) == 1) { p[L] = (i - L) * 2 + 1; return dp[L][R] = 1; } } } return dp[L][R] = -1; } int main() { scanf("%d", &n); int flag = 1; for(int i = 1; i <= n; i++) { scanf("%d%d", &l[i], &r[i]); if(l[i] % 2 == 0) l[i]++; if(r[i] % 2 == 0) r[i]--; if(l[i] > r[i]) { flag = 0; } l[i] = (l[i] + 1) / 2; r[i] = (r[i] + 1) / 2; } if(flag == 0) { printf("IMPOSSIBLE\n"); return 0; } dfs(1, n); if(dp[1][n] != 1) { printf("IMPOSSIBLE\n"); return 0; } int j = 0; for (int i = 1 ; i <= n; i++){ while(s[j]) ++j; s[j] = '('; s[j + p[i]] = ')'; } puts(s); return 0; }
Codeforces Round #288 (Div. 2)
标签:
原文地址:http://blog.csdn.net/sdj222555/article/details/43245421