2013年腾讯编程马拉松初赛第2场(3月22日)解题参考
2013-03-23 15:27
381 查看
HDU4510:小Q系列故事——为什么时光不能倒流
参考思路:模拟。参考程序:
#include<cstdio> #include<cstring> int main() { int nt; scanf("%d", &nt); while( (nt --) > 0 ) { int H, M, S; char c; scanf("%d%c%d%c%d", &H, &c, &M, &c, &S); int h, m, s; scanf("%d%c%d%c%d", &h, &c, &m, &c, &s); while( h || m || s ) { S --; if( S == -1 ) { S = 59; M --; if( M == -1 ) { M = 59; H --; if( H == -1 ) { H = 23; } } } s --; if( s == -1 ) { s = 59; m --; if( m == -1 ) { m = 59; h --; if( h == -1 ) { h = 0; } } } } printf("%02d:%02d:%02d\n", H % 12, M, S); } return 0; }
HDU4511:小明系列故事——女友的考验
参考思路:Not Finished。参考程序:Not Submitted。
HDU4512:吉哥系列故事——完美队形I
参考思路:基本思路是去尝试枚举中间的一个数(奇数个)和两个数(偶数个),将原数组A逆序,得到新数组B,然后依次计算数组 A[1...x] 和 B[1...y] 的最长公共上升子序列,其中 x 和 y 满足表达式 x + y = N,这里 N 表示总人数。
求最长公共上升子序列的状态转移方程:
如果A[ i ] != B[ j ],那么有 DP[ i ][ j ] = DP[ i - 1 ][ j ];
如果 A[ i ] = B[ j ],那么有 DP[ i ][ j ] = max { DP[ i - 1 ][ k ] + 1 | 1 <= k < j and B[ k ] < B[ j ] }。
参考程序:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxN = 200 + 2; int N, h[maxN]; int A[maxN], B[maxN]; int dp[maxN][maxN]; int gao(int l, int r) { // dp[i][j] = dp[i-1][j] if A[i] != B[j] // else dp[i][j] = max { dp[i-1][k] + 1 | 1 <= k < j and B[k] < B[j] } memset(dp, 0, sizeof(dp)); for(int i = 1; i <= l; i ++) { int cp = 0; for(int j = 1; j <= r; j ++) { dp[i][j] = dp[i - 1][j]; if( A[i] == B[j] ) dp[i][j] = cp + 1; else if( A[i] > B[j] ) cp = max(cp, dp[i - 1][j]); } } int res = 0, idx = 0; for(int k = 1; k <= r; k ++) { if( dp[l][k] > res ) { res = dp[l][k]; idx = k; } } res <<= 0x1; while( r > idx ) { if( B[r] > B[idx] ) { res ++; break; } r --; } return res; } int main() { int nt; scanf("%d", &nt); while( (nt --) > 0 ) { scanf("%d", &N); for(int i = 1; i <= N; i ++) scanf("%d", h + i); for(int i = 1; i <= N; i ++) { A[i] = h[i]; B[i] = h[N + 1 - i]; } A[0] = B[0] = 0; int res = 1; for(int d = 1; d <= N; d ++) res = max(res, gao(d, N - d)); printf("%d\n", res); } return 0; }
HDU4513:吉哥系列故事——完美队形II
参考思路:同样地,基本思路也是去枚举中间的一个数(奇数个)和两个数(偶数个),只是此时不能采用DP求解,显然是因为复杂度太高,考虑一下,假设枚举到某一数,如果能够在接近 O(logN) 复杂度内求出需要的结果,显然是可以的。那么,怎么求呢?我采用的方法是:Hash + 二分。
具体做法:
对于数组中的每一个 h[ i ],统计序列 { h[ s ] <= h[ s + 1 ] <= ... <= h[ i - 1 ] } 最长的长度,记为 pre[ i ];
对于数组中的每一个 h[ i ],统计序列 { h[ i + 1 ] >= h[ i + 2 ] >= ... >= h[ e ] } 最长的长度,记为 nxt[ i ];
将 h[ i ] 映射成 (h[ i ] ^ 3) + (h[ i ] ^ 2) + h[ i ],这里做的目的是避免后面容易产生Hash碰撞;
计算 S[ i ] = Sum { (h[ k ] ^ 3) + (h[ k ] ^ 2) + h[ k ] | 1 <= k <= i };
考虑奇数的数(偶数同理),假设当前枚举到的中间数是第 i 个,即 h[ i ] 处。现在已经知道它左边可以延伸 pre[ i ]个,右边可以延伸 nxt[ i ] 个,显然,如果以 h[ i ] 作为中间数,最多左右延伸 T[ i ] = min { pre[i], nxt[i] }个,最少延伸0个。如果可以延伸 k (k <= T[ i ])个,显然也可以延伸 (k - 1)个;如果不可以延伸 k 个,显然也不可以延伸 (k + 1) 个,即满足二分性质;
在前一步骤的分析下,对于每一个枚举到的位置,去二分最大的延伸个数。此时,还需要解决一个问题,二分的时候,怎么判定合法性呢?这里可以就利用 S[ i ],假设当前左右延伸k个数,对于当前枚举的中心位置 h[ i ],如果有 S[ i - 1 ] - S[ i - k - 1] = S[ i + k ] - S[i - 1],就表示可以延伸k个数。(注意,各下角标一定要合法)
参考程序:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxN = 100000 + 10; const int Mod = 1000000007; int N; int h[maxN]; int pre[maxN], nxt[maxN]; int main() { int nt; scanf("%d", &nt); while( (nt --) > 0 ) { scanf("%d", &N); for(int i = 0; i < N; i ++) scanf("%d", h + i); for(int i = 0; i < N; i ++) h[i] = h[i] * h[i] * h[i] + h[i] * h[i] + h[i]; for(int i = 0; i < N; i ++) pre[i] = nxt[i] = 0; for(int i = 1; i < N; i ++) if( h[i] >= h[i - 1] ) pre[i] = pre[i - 1] + 1; for(int i = N - 2; i >= 0; i --) if( h[i] >= h[i + 1] ) nxt[i] = nxt[i + 1] + 1; for(int i = 1; i < N; i ++) h[i] = (h[i] + h[i - 1]) % Mod; int res = 1; int l, r, mid, p, q; for(int i = 0; i + 1 < N; i ++) { if( pre[i] >= 1 && nxt[i] >= 1 ) { l = 1; r = min(pre[i], nxt[i]); while( l <= r ) { mid = (l + r) >> 1; p = i - mid; q = i + mid; if( p == 0 ) p = h[i - 1]; else p = h[i - 1] - h[p - 1]; q = h[q] - h[i]; p %= Mod; if( p < 0 ) p += Mod; q %= Mod; if( q < 0 ) q += Mod; if( p == q ) { res = max(2 * mid + 1, res); l = mid + 1; } else r = mid - 1; } } if( i == 0 ) p = h[i]; else p = h[i] - h[i - 1]; p %= Mod; if( p < 0 ) p += Mod; q = h[i + 1] - h[i]; q %= Mod; if( q < 0 ) q += Mod; if( p == q ) { res = max(res, 2); if( pre[i] >= 1 && nxt[i + 1] >= 1 ) { l = 1; r = min(pre[i], nxt[i + 1]); while( l <= r ) { mid = (l + r) >> 1; p = i - mid; if( p == 0 ) p = h[i - 1]; else p = h[i - 1] - h[p - 1]; p %= Mod; if( p < 0 ) p += Mod; q = i + 1 + mid; q = h[q] - h[i + 1]; q %= Mod; if( q < 0 ) q += Mod; if( p == q ) { res = max(res, 2 + 2 * mid); l = mid + 1; } else r = mid - 1; } } } } printf("%d\n", res); } return 0; }
HDU4514:湫湫系列故事——设计风景线
参考思路:题意:给一个无向图,判断其是否存在环,如果存在环,就输出YES,这里对环并没有什么限制条件;如果这个无向图不存在环,那么,求它所有联通分量的最大生成树的费用值,并求和输出。
首先,判断可以用并查集;然后,对Kruskal算法稍作修改,就可以求出和(建议自己分析)。
参考程序:
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int maxN = 100000 + 2; const int maxM = 1000000 + 2; struct Edge { int u, v, w; inline bool operator<(const Edge &s) const { return w > s.w; } inline void in() { scanf("%d %d %d", &u, &v, &w); if( u > v ) swap(u, v); } }; Edge A[maxM]; int N, M; int fat[maxN]; int find(int x) { if( x == fat[x] ) return x; return (fat[x] = find(fat[x])); } bool merge(int u, int v) { int fu = find(u), fv = find(v); if( fu != fv ) return true; return false; } int len[maxN]; int calc() { for(int i = 1; i <= N; i ++) fat[i] = i; sort(A, A + M); for(int i = 1; i <= N; i ++) len[i] = 0; int res = 0; for(int i = 0; i < M; i ++) { if( merge(A[i].u, A[i].v) ) { int du = find(A[i].u); int dv = find(A[i].v); len[du] += len[dv] + A[i].w; res = max(res, len[du]); fat[dv] = du; } else return -1; } return res; } int main() { while( scanf("%d %d", &N, &M) == 2 ) { for(int i = 0; i < M; i ++) A[i].in(); int res = calc(); if( res == -1 ) printf("YES\n"); else printf("%d\n", res); } return 0; }
OpenSpirit @ SWJTU
相关文章推荐
- 2013年腾讯编程马拉松初赛第0场(3月20日)解题参考
- 2013年腾讯编程马拉松初赛第1场(3月21日)解题参考
- 2013腾讯编程马拉松初赛第二场(3月22日) 小Q系列故事——为什么时光不能倒流 ---好水!!
- 2013腾讯编程马拉松初赛第一场(3月21日) 解题报告 (HDU 4505 HDU4506 HDU4507 HDU4508 HDU4509)
- 2013腾讯马拉松编程初赛3月22日1001
- 2013腾讯马拉松编程初赛3月22日1001
- 湫湫系列故事——设计风景线(并查集) [2013腾讯编程马拉松初赛第二场(3月22日)]
- 2013腾讯编程马拉松初赛第二场(3月22日) 小Q系列故事——为什么时光不能倒流 ---好水!!
- 2013腾讯编程马拉松初赛第2场(3月22)(HDU 4510 HDU4511 HDU4512 HDU4513 HDU4514)
- 2013腾讯编程马拉松初赛第二场(3月22日) 小Q系列故事——为什么时光不能倒流
- 2013腾讯编程马拉松初赛第二场(3月22日) 小Q系列故事——为什么时光不能倒流 ---好水!!
- 2013腾讯编程马拉松初赛第五场(3月25日)
- 2013腾讯编程马拉松初赛第一场(3月21日) 湫湫系列故事——减肥记II ----线段树
- 2013腾讯编程马拉松初赛第一场(3月21日)小明系列故事——师兄帮帮忙
- 2013腾讯编程马拉松初赛第〇场
- HDU 4505 小Q系列故事——电梯里的爱情 2013腾讯编程马拉松初赛第一场第一题
- HDU 4525 威威猫系列故事——吃鸡腿 2013腾讯编程马拉松初赛第五场第一题
- 2013腾讯编程马拉松初赛第〇场(3月20日) 吉哥系列故事——临时工计划---带权重的区间规划
- [置顶] 2013腾讯编程马拉松初赛第3场(3月23)(HDU 4515 HDU4516 HDU4517 HDU4517 HDU4519)
- 2013腾讯编程马拉松初赛第5场(3月25)(HDU 4525 HDU4526 HDU4527 HDU4528 HDU4529)