您的位置:首页 > 编程语言

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐