[USACO Training] Section 2.3

PROB Longest Prefix

给定一些(length 1..200)小字符串(length 1..10)和一个大字符串(length 1..200,000),求大字符串的前缀中能由小字符串拼接得到的最大长度。

递推,朴素地比较就可以。看到字符串这么长,朴素地比较有点虚……想到NOI 2016 Day1 T1和前辈用strcmp水阿狸的打字机的故事,我决定试一试。正如ANALYSIS所言,字符串比较的最坏情况不易达到,常数也小;可以用Trie优化。

写以下代码的时候我忘记了strncmp这个东西。

/*
ID: chrt2001
PROG: prefix
LANG: C++
*/
#include <cstdio>
#include <cstring>
using namespace std;
char p[201][11], s[200001];
int l[200];
bool f[200000];
int main()
{
    freopen("prefix.in", "r", stdin);
    freopen("prefix.out", "w", stdout);
    int m = 0, n = 0, ans = 0;
    while ((~scanf("%s", p[m])) && p[m][0] != '.') {
        l[m] = strlen(p[m]);
        ++m;
    }
    while (~scanf("%s", s+n))
        while (s[n])
            ++n;
    char c;
    for (int i = 0; i < n; ++i) {
        c = s[i+1];
        s[i+1] = 0;
        for (int j = 0; j < m; ++j)
            if (i+1 >= l[j] && (i-l[j] == -1 || f[i-l[j]]) && strcmp(s+i-l[j]+1, p[j]) == 0) {
                f[i] = true;
                ans = i+1;
                break;
            }
        s[i+1] = c;
    }
    printf("%d\n", ans);
    return 0;
}

PROB Cow Pedigrees

求一共有N个(3 <= N < 200)结点、高度为K(1 < K < 100)、每个结点有0或2个儿子的树的个数,对9901取模。

联想到两个东西:一是卡特兰数,二是用高度小于等于K的减去高度小于等于(K-1)的得到高度等于K的。

本题的名字和某wiki很像。

/*
ID: chrt2001
PROG: nocows
LANG: C++
*/
#include <cstdio>
using namespace std;
const int M = 9901;
int f[100][200];

int main()
{
    freopen("nocows.in", "r", stdin);
    freopen("nocows.out", "w", stdout);
    int n, k;
    scanf("%d %d", &n, &k);
    if (n % 2 == 0 || k > n/2+1) {
        puts("0");
        return 0;
    }
    f[1][1] = 1;
    for (int i = 2; i <= k; ++i) {
        f[i][1] = 1;
        for (int j = 3; j <= n; j += 2)
            for (int t = 1; t < j-1; t += 2)
                f[i][j] = (f[i][j] + f[i-1][t]*f[i-1][j-1-t]) % M;
    }

    printf("%d\n", (f[k][n] - f[k-1][n] + M) % M);
    return 0;
}

PROB Zero Sum

求在1~n <= 9之间填+-或不填,结果为0的表达式,按字典序输出。

枚举一下就好。我是一边枚举一边计算的,也可以像ANALYSIS那样最后再算。

/*
ID: chrt2001
PROG: zerosum
LANG: C++
*/
#include <cstdio>
using namespace std;
enum op {BEGIN, BLANK=' ', PLUS='+', MINUS='-'};
char s[10];
int n;

inline void print()
{
    putchar('1');
    for (int i = 2; i <= n; ++i)
        printf("%c%d", s[i], i);
    putchar('\n');
}

// k ~ 当前数字;x ~ 上一个数;r ~ 先前的结果;pre ~ 上一个非空操作;now ~ 当前操作
void search(int k, int x, int r, op pre, op now)
{
    s[k] = char(now);
    switch (now) {
        case BLANK:
        if (pre == PLUS)
            r += 9*x + k;
        else
            r -= 9*x + k;
        x = 10*x + k;
        break;

        case PLUS:
        r += k;
        x = k;
        break;

        case MINUS:
        r -= k;
        x = k;
    }

    if (k == n) {
        if (r == 0)
            print();
        return;
    }

    op o = now == BLANK ? pre : now;

    search(k+1, x, r, o, BLANK);
    search(k+1, x, r, o, PLUS);
    search(k+1, x, r, o, MINUS);
}

int main()
{
    freopen("zerosum.in", "r", stdin);  
    freopen("zerosum.out", "w", stdout);
    scanf("%d", &n);
    search(1, 0, 0, BEGIN, PLUS);
    return 0;
}

PROB Money Systems

那个作为错误贪心范例的经典DP问题。

注意这里隐含着滚动数组,所以不能把两维循环的顺序搞反。(开始我搞反了QAQ)

/*
ID: chrt2001
PROG: money
LANG: C++
*/
#include <cstdio>
using namespace std;
typedef long long ll;
const int MAX_N = 10000, MAX_V = 25;
ll f[MAX_N+1];
int c[MAX_V];
int main()
{
    freopen("money.in", "r", stdin);
    freopen("money.out", "w", stdout);
    int v, n;
    scanf("%d %d", &v, &n);
    for (int i = 0; i < v; ++i)
        scanf("%d", &c[i]);
    f[0] = 1;
    for (int i = 0; i < v; ++i)
        for (int j = 1; j <= n; ++j)
            f[j] += (j >= c[i] ? f[j-c[i]] : 0);
    printf("%lld\n", f[n]);
    return 0;
}

PROB Controlling Companies

公司A拥有公司B当且仅当满足下述条件中至少一个:
- A=B。
- A拥有B大于50%的股权。
- A拥有的公司中拥有B的股权之和大于50%。

给定一些股权所有信息,求所有的拥有关系,不用输出自己拥有自己。公司不超过100个。

从每个点开始BFS。用队列维护它拥有的点,向外更新。

ANALYSIS用了诡异的递归。

/*
ID: chrt2001
PROG: concom
LANG: C++
*/
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_N = 100, MAX_P = 50;
bool control[MAX_N+1][MAX_N+1];
int n, M[MAX_N+1][MAX_N+1], p[MAX_N+1];
queue<int> Q;

void search(int x)
{
    memset(p+1, 0, sizeof(int)*n);
    Q.push(x);
    control[x][x] = true;
    int u;
    while (!Q.empty()) {
        u = Q.front();
        Q.pop();
        for (int v = 1; v <= n; ++v)
            if (M[u][v]) {
                p[v] += M[u][v];
                if (p[v] > MAX_P && !control[x][v]) {
                    Q.push(v);
                    control[x][v] = true;
                }
            }
    }
}

int main()
{
    freopen("concom.in", "r", stdin);
    freopen("concom.out", "w", stdout);
    int m;
    scanf("%d", &m);
    for (int i = 0; i < m; ++i) {
        int x, y, p;
        scanf("%d %d %d", &x, &y, &p);
        n = max(n, max(x, y));
        M[x][y] += p;
    }
    for (int i = 1; i <= n; ++i)
        search(i);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            if (control[i][j] && i != j)
                printf("%d %d\n", i, j);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值