Tutorials for The 10th SWJTU Collegiate Programming Contest - Qualification Round

【A题】

类型:模拟题

注意点:

1、用字符串存储数字,存储字符串长度应大于1000,因为含1000字符的字符串实际占用空间有1001个字节。

2、计算加法从最低位开始,而最低位对应字符串末尾,可先逆转字符串,即“123”变为“321”,再从第0位开始计算。

3、假定两个字符串的其中的最大长度为L,那么答案字符串的长度可能是L+1,如“1”和“9”,其和为“10”。

4、避免出现无效的前导“0”,如结果不可能是“0123”。

参考程序:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

#define maxN (1024)

char A[maxN], B[maxN], C[maxN];

void gao()
{
    int i, j = 0, la = strlen(A), lb = strlen(B), lc = 0;
    assert(la <= 1000 && lb <= 1000);
    memset(C, 0, sizeof(C)); strrev(A); strrev(B);
    for(i = 0; i < la || i < lb; i ++) {
        if( i < la && i < lb ) {
            C[lc] = '0' + ((A[i] - '0' + B[i] - '0' + j) % 10);
            j =  ((A[i] - '0' + B[i] - '0' + j) / 10); lc ++;
        } else {
            if( i < la ) {
                C[lc] = '0' + ((A[i] - '0' + j) % 10);
                j =  ((A[i] - '0' + j) / 10); lc ++;
            } else {
                C[lc] = '0' + ((B[i] - '0' + j) % 10);
                j =  ((B[i] - '0' + j) / 10); lc ++;
            }
        }
    } C[lc] = '0' + j; lc ++; 
    while( (lc - 1 > 0) && (C[lc - 1] == '0') ) 
        lc --; C[lc] = 0; strrev(C);
}

int main()
{
    int k, nt;
    scanf("%d", &nt);
    for(k = 0; k < nt; k ++) {
        scanf("%s %s", A, B);
        
        if( k )
            printf("\n");
        printf("Case %d:\n", k + 1);
        printf("%s + %s = ", A, B);
        
        gao();
        
        printf("%s\n", C);
    }
    return 0;
}


【B题】

类型:最长公共子序列 (Longest Common Subsequence, LCS)

注意点:

1、请保证存储空间超过1000,有些同学一直是开到1000,这是没有理解字符串如何存储。

2、如果 S[ i ] = S[ j ],那么 DP[ i ][ j ] = DP[ i - 1 ][ j - 1 ] + 1;否则,DP[ i ][ j ] = max(DP[ i-1 ][ j ], DP[ i ][ j-1 ])。

参考代码: 

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

#define maxN (1024)

char A[maxN], B[maxN];
short dp[maxN][maxN];

int gao()
{
    int i, j, la = strlen(A), lb = strlen(B);
    assert(la <= 1000 && lb <= 1000);
    for(i = 0; i < la; i ++) {
        for(j = 0; j < lb; j ++) {
            if( i == 0 || j == 0 ) {
                if( A[i] == B[j] )
                    dp[i][j] = 1;
                else {
                    if( i == 0 )
                        dp[i][j] = dp[i][j - 1];
                    else
                        dp[i][j] = dp[i - 1][j];
                }
            } else {
                if( A[i] == B[j] )
                    dp[i][j] = 1 + dp[i - 1][j - 1];
                else {
                    dp[i][j] = dp[i - 1][j];
                    if( dp[i][j] < dp[i][j - 1] )
                        dp[i][j] = dp[i][j - 1];
                }
            }
        }
    }
    return (int)dp[la - 1][lb - 1];
}

int main()
{
    while( scanf("%s %s", A, B) == 2 )
        printf("%d\n", gao());
    return 0;
}

【C题】

类型:素数筛法+二分

基本思路:先筛选出给定范围内的所有素数,然后计算连续区间内的素数和,判断其是否也为素数,如果是,则标记为可写成连续素数的和,否则不是。

参考代码:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

#define maxN (500000)

bool vst[maxN];
int prime[maxN];
int total_prime = 0;

void get_prime()
{
    int i, j, up = maxN;
    memset(vst, 0, sizeof(vst));
    prime[total_prime ++] = 2;
    for(i = 3; i < up; i += 2) {
        if( vst[i] == false ) {
            prime[total_prime ++] = i;
            for(j = i << 1; j < up; j += i)
                vst[j] = true;
        }
    }
    //printf("total_prime = %d, (%d)\n", total_prime, prime[total_prime - 1]);
}

long long sum[50000];
bool sgn[maxN];

void gao()
{
    int i, j, l, r, m, t; long long tmp; sum[0] = prime[0];
    for(i = 1; i < total_prime; i ++)
        sum[i] = sum[i - 1] + prime[i];
    memset(sgn, 0, sizeof(sgn));
    for(i = 1; i < total_prime && sum[i] < maxN; i ++)
        if(  vst[sum[i]] == false  )
            sgn[sum[i]] = true;
    for(i = 1; i < total_prime; i ++) {
        l = i + 1, r = total_prime - 1; t = -1;
        tmp = sum[i - 1];
        while( l <= r ) {
            m = (l + r) >> 1;
            if( sum[m] - tmp > maxN )
                r = m - 1;
            else
                l = m + 1, t = m;
        }
        if( t != -1 ) {
            for(j = i + 1; j <= t; j ++)
                if( vst[sum[j] - tmp] == false )
                    sgn[sum[j] - tmp] = true;
        }
    }
    j = 0; for(i = 0; i < total_prime; i ++)
        if( sgn[ prime[i] ] ) prime[j ++] = prime[i];
    total_prime = j; for(i = maxN - 1; i >= 0 && total_prime > 0; total_prime --)
        while( i >= prime[total_prime - 1] )
            prime[i --] = prime[total_prime - 1];
    while( i >= 0 ) prime[i --] = 0;
}

int main()
{
//#define LOCAL_JUDGE
#ifdef LOCAL_JUDGE
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
#endif
    get_prime(); gao();
    int N; while( scanf("%d", &N) == 1 ) {
        assert(N > 0 && N < 500000);
        printf("%d\n", prime[N]);
    }
#ifdef LOCAL_JUDGE
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}

【D题】

类型:水题

注意点:

1、不足3位补0。

参考代码:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

#define maxN (500000)

int main()
{
//#define LOCAL_JUDGE
#ifdef LOCAL_JUDGE
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
#endif
    int A, B, C; while( scanf("%d %d", &A, &B) == 2 ) {
        assert(A >= 0 && A <= 10000 && B >= 0 && B <= 10000);
        if( A == 0 && B == 0 )
            break;
        C = A % 1000; while( -- B ) C = (C * A) % 1000;
        printf("%03d\n", C);
    }
#ifdef LOCAL_JUDGE
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}

【E题】

类型:水题

参考代码:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

#define maxN (1024)

char marry[maxN], carry[maxN];

int main()
{
//#define LOCAL_JUDGE
#ifdef LOCAL_JUDGE
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
#endif
    while( scanf("%s %s", marry, carry) == 2 ) {
        int lm = strlen(marry), lc = strlen(carry);
        assert(lm == lc);
        int wins_marry = 0, wins_carry = 0;
        for(int i = 0; i < lm; i += 2) {
            if( marry[i] == '[' ) {
                if( carry[i] == '8' )
                    wins_marry ++;
                if( carry[i] == '[' )
                    ;
                if( carry[i] == '(' )
                    wins_carry ++;
            }
            if( marry[i] == '(' ) {
                if( carry[i] == '[' )
                    wins_marry ++;
                if( carry[i] == '(' )
                    ;
                if( carry[i] == '8' )
                    wins_carry ++;
            }
            if( marry[i] == '8' ) {
                if( carry[i] == '(' )
                    wins_marry ++;
                if( carry[i] == '8' )
                    ;
                if( carry[i] == '[' )
                    wins_carry ++;
            }
        }
        printf("%s\n", wins_marry > wins_carry ? "Marry" : \
               (wins_marry == wins_carry ? "Equal" : "Carry") );
    }
#ifdef LOCAL_JUDGE
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}

【F题】

类型:数据结构 (RMQ 或者 线段树等)

基本思路:缩点 + RMQ 静态查询或者线段树动态查询

参考代码:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxn = 100000 + 2;

struct node {
	int val;
	int low, high;
};
node A[maxn];
int nA, N, Q;

node Tree[maxn << 2];

int Build_Tree(int left, int right, int idx) {
	Tree[idx].low = left;
	Tree[idx].high = right;

	if(left == right) return (Tree[idx].val = A[left].high - A[left].low + 1);

	int mid = (left + right) >> 1;
	int _left = Build_Tree(left, mid, idx << 1);
	int _right = Build_Tree(1 + mid, right, 1 + (idx << 1));

	return (Tree[idx].val = max(_left, _right));
}

int Query_Tree(int left, int right, int idx) {
	if(left > right) return 0;

	if(left == Tree[idx].low && right == Tree[idx].high) return Tree[idx].val;

	int mid = (Tree[idx].low + Tree[idx].high) >> 1;
	if(right <= mid) return Query_Tree(left, right, idx << 1);
	if(mid < left) return Query_Tree(left, right, 1 + (idx << 1));

	return max(Query_Tree(left, mid, idx << 1), Query_Tree(1 + mid, right, 1 + (idx << 1)));
}

int bs(node A[], int low, int high, int &key) {
	int mid;

	while(low <= high) {
		mid = (low + high) >> 1;

		if(A[mid].low <= key && A[mid].high >= key) 
            return mid;
		
		if(A[mid].low > key) high = mid - 1;
		else low = mid + 1;
	}

	return -1;
}

int main() {
	while( scanf("%d", &N) == 1 && N ) {
		scanf("%d", &Q);

		nA = 1; int val;
		for(int i = 1; i <= N; ++i) {
			scanf("%d", &val);
			
			if(i == 1) { A[nA].val = val; A[nA].low =  A[nA].high = i; }
			else {
				if(val == A[nA].val ) A[nA].high = i;
				else { ++nA; A[nA].val = val; A[nA].low =  A[nA].high = i; }
			}
		}
		
		Build_Tree(1, nA, 1);

		int x, y, fx, fy;
		for(int i = 0; i < Q; ++i) {
			scanf("%d%d", &x, &y);

			fx = bs(A, 1, nA, x);
			fy = bs(A, 1, nA, y);

			if(fx == fy) printf("%d\n", y - x + 1);
			else printf("%d\n", max( - x + A[fx].high + 1, \
                           max( - A[fy].low + y + 1, Query_Tree(fx + 1, fy - 1, 1))));
		}
	}
	return 0;
}

【G题】

类型:简单题

基本思路:直接判断第N-1天所能学到的单元数即可,这是因为第K+1天能看到单元数不会少于第K天。

参考代码:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
    int nt, idx = 0; scanf("%d", &nt);
    assert(nt <= 10000); int i, tmp, N;
    while( (nt --) > 0 ) {
        scanf("%d", &N); //assert(N >= 1 && N <= 1000000007);
        tmp = 0, i = N - 1;
        if( i >= 30 ) tmp ++;
        if( i >= 15 ) tmp ++;
        if( i >= 7  ) tmp ++;
        if( i >= 4  ) tmp ++;
        if( i >= 2  ) tmp ++;
        if( i >= 1  ) tmp ++;
        if( i >= 0  ) tmp ++;
        printf("Case #%d: %d\n", ++idx, tmp);
    }
    return 0;
}

【H题】

类型:枚举+推导

基本思路:

1、其实如果你先去做I题,可以发现 p = 1, 2, 3时都是有限的,4是无限的。

2、现在我们来证明3个命题:

定义:S(p) 表示回文长度为p的所有满足条件的字符串长度最大的字符串的长度。

(1) 如果 S(p) 无限,那么对于任意 k > 0,有 S(p + 2 * k) 也是无限。

证明:假设对应S(p)的无限长的某个字符串是 T(p),不是一般性,令T(p)的某一前缀是回文串且长度为p,显然可以在T(p)的前面增加一个字符,使得T(p)的前缀是回文串且长度为p+2,此时成功构造了 S(p + 2),由于 S(p) 无限,所以S(p + 2) 也无限长。以此类推,可证 S(p + 2 * k) 也是无限。


(2) 如果S(p) 无限,那么形同 "aaa...aaa" (长度为p)  或 "bbb...bbb" (长度为p) 一定可以存在某个合法的 T(p) 中,而且只可能是 T(p) 的前缀。

证明:(I) 假设存在这样的"aaa...aaa" (长度为p)  或 "bbb...bbb" 是 T(p) 的非前缀,根据容斥原理,显然 "aaa...aaa" 或 "bbb...bbb" 的左侧或者右侧存在一个字符与"a" 或者 "b" 相等,这样它的最大回文长度为 p+1,与长度为p矛盾,故如果存在,则"aaa...aaa" 或 "bbb...bbb" 一定是 T(p) 的前缀。(II) 显然一定存在这样的 T(p) 不包含"aaa...aaa" (长度为p)  或 "bbb...bbb" ,而我们总可以在它的前面补足"a" 或"b",使得"aaa...aaa" 或 "bbb...bbb" 是它的前缀,而且仍然是合法的字符串。结合(I)、(II),命题得证。


(3) 如果S(p) 无限,那么S(p + 1) 无限。

证明:如果S(p) 无限,那么可以在包含形同 "aaa...aaa" (长度为p)  或 "bbb...bbb" (长度为p) 的T(p)的前面添加1个"a"或"b",使得S(p+1) 无限。


参考程序:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
    int p; while( scanf("%d", &p) == 1 ) {
        assert(p >= 1 && p <= 100);
        switch( p ) {
        case 1:
            puts("2"); break;
        case 2:
            puts("4"); break;
        case 3:
            puts("8"); break;
        default:
            puts("Unlimited!"); break;
        }
    }
    return 0;
}

【I题】

类型:搜索题 (DFS)

参考代码:

/*
 * Author: Wu Hanzhou
 * Time: 2014/4/11
 * Language: C++
 * Role: Check for SWJTU-CPC-2014
 */

#include<stdio.h>
#include<string.h>
#include<assert.h>

const int maxN = 200 + 2;
const int up   = 200;
const int  p   = 5;

char str[maxN];

bool judge(int c, int NN)
{
    int tot = 1;
    for(int i = c - 1, j = c + 1; i >= 0 && j <= NN; i --, j ++)
        if( str[i] == str[j] )
            tot += 2;
        else
            break;
    if( tot > p )
        return true;
    if( c + 1 <= NN && str[c] == str[c + 1] ) {
        tot = 2;
        for(int i = c - 1, j = c + 2; i >= 0 && j <= NN; i --, j ++)
            if( str[i] == str[j] )
                tot += 2;
            else
                break;
        if( tot > p )
            return true;
    }
    return false;
}

bool dfs(int idx)
{
    if( idx == 200 )
        return true;
    bool chk = true;
    str[idx] = 'a';
    for(int i = 0; i <= idx && chk; i ++)
        if( judge(i, idx) )
            chk = false;
    if( chk ) {
        chk = dfs(idx + 1);
        if( chk )
            return true;
    }
    chk = true; str[idx] = 'b';
    for(int i = 0; i <= idx && chk; i ++)
        if( judge(i, idx) )
            chk = false;
    if( chk ) {
        chk = dfs(idx + 1);
        if( chk )
            return true;
    }
    return false;
}

int main()
{
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
    dfs(0); int n; str[200] = 0;
    //printf("%s\n", str);
    // aaaaa baab babaaab babaaab babaaab babaaab babaaab babaaab babaaab babaaab babaaab babaaab
    // babaaab babaaab babaaab babaaab babaaab babaaab babaaab babaaab babaaab babaaab babaaab 
    // babaaab babaaab babaaab babaaab babaaab babaaab ba
    while( scanf("%d", &n) == 1 ) {
        assert(n > 0 && n < 200);
        printf("%c\n", str[n - 1]);
    }
    return 0;
}



能力有限,如有错误,敬请指出,有疑问,也可联系:[email protected]

你可能感兴趣的:(校赛)