[USACO Training] Section 1.5

TEXT Binary Numbers

关于二进制和十六进制之间的相互转换,学到了一个窍门:记住10(10)=1010(2)=A(16),12(10)=1100(2)=C(16),15(10)=1111(2)=F(16)。

复制一点东西备用。还有一篇文章:Advanced bit manipulation-fu

a |= 0x20;      /* turn on bit 0x20 */
a &= ~0x20;        /* turn off bit 0x20 */
a ^= 0x20;      /* toggle bit 0x20 */
if (a & 0x20) {
    /* then the 0x20 bit is on */
}
             Binary
Value        Sample             Meaning
  x         00101100        the original x value
x & -x      00000100        extract lowest bit set
x | -x      11111100        create mask for lowest-set-bit & bits to its left
x ^ -x      11111000        create mask bits to left of lowest bit set
x & (x-1)   00101000        strip off lowest bit set
                            --> useful to process words in O(bits set)
                                instead of O(nbits in a word)
x | (x-1)   00101111        fill in all bits below lowest bit set
x ^ (x-1)   00000111        create mask for lowest-set-bit & bits to its right
~x & (x-1)  00000011        create mask for bits to right of lowest bit set
x | (x+1)   00101101        toggle lowest zero bit
x / (x&-x)  00001011        shift number right so lowest set bit is at bit 0

PROB Number Triangles

没错,就是传说中动态规划的经典题——数字三角形。给一个数字三角形,从顶层出发,往左下或右下走,直到底层,问:路径上数之和的最大值是多少?

我的代码多开了点数组以简化边界的处理。ANALYSIS中有两种写法:一种从底层开始往上递推,另一种从顶层开始往下递推,第二种我没考虑过。

昨天下午去了学校机房,写了这题,交了,然后忘记保存代码了……

/*
ID: chrt2001
PROG: numtri
LANG: C++
*/
#include <cstdio>
#include <algorithm>
using namespace std;
int T[1001][1001], f[1001][1001];
int main()
{
    freopen("numtri.in", "r", stdin);
    freopen("numtri.out", "w", stdout);
    int R;
    scanf("%d", &R);
    for (int i = 0; i < R; ++i)
        for (int j = 0; j <= i; ++j)
            scanf("%d", &T[i][j]);
    for (int i = R-1; i >= 0; --i)
        for (int j = 0; j <= i; ++j)
            f[i][j] = T[i][j] + max(f[i+1][j], f[i+1][j+1]);
    printf("%d\n", f[0][0]);
    return 0;
}

PROB Prime Palindromes

找不小于a、不大于b的回文素数,5 <= a < b <= 100,000,000。

十个月前在NOI官网的OJ里做过:11:回文素数

这个问题是TYQ带给我的。应该枚举回文还是素数?我选择枚举回文:一是因为根据素数定理进行估计,范围内的素数是百万级别的(以USACO Training中的这道题为例),而优化后回文的数量是小于一万的;二是因为数据范围太大,枚举质因子太慢,筛法也在时空上难以胜任。NOI官网那题,只需要N位的回文素数,用筛法去打素数表不太划算。

优化是指位数为偶数的回文素数只有11。以前是测试程序的时候发现了这个现象,百度告诉我这是事实,但一直不会证。昨天醒悟……用同余的性质不是可以证出被11整除的判定方法吗?奇数位数字之和等于偶数位数字之和。忽略了10模11不但同余于10,还同余于-1。

提交记录有5条:

2015-10-25 16:52:00 Wrong Answer // 题目看错:只要输出N位的回文素数,我输出了1~N位;此时尚未发觉
2015-10-25 16:56:21 Wrong Answer // debug:输出11之前要判n是否大于1
2015-10-25 22:41:05 Wrong Answer // 发现题目看错,修改;把大小为6000的数组改为5172
2015-10-25 22:42:36 Wrong Answer // debug:特判的时候输出0没换行
2015-10-25 22:51:07 Accepted // debug:为了格式先输出了prime[0],所以某处下标应从1开始;去掉了上一版本加的那个换行

印象中写得很艰难……这比我记忆中的顺利多了。

当时的AC代码:

#include <iostream>
#include <cmath>

int createNum(int a, int width);
bool isPrime(int x);

using namespace std;

int prime[5172];

int main()
{
    int n, count = 0, x;
    cin >> n;
    if (n == 2)
        cout << "1" << endl << "11";
    else if (n % 2 == 0) {
        cout << "0" << endl;
    } else {
        const int max = pow(10, (n+1)/2);
        for (int i = pow(10, (n+1)/2 - 1); i < max; ++i) {
            x = createNum(i, (n+1)/2 - 1);
            if (isPrime(x))
                prime[count++] = x;
        }
        cout << count << endl;
        if (count) {
            cout << prime[0];
            for (int i = 1; i < count; ++i)
                cout << ' ' << prime[i];
        }
    }
    return 0;
}

int createNum(int a, int width)
{
    int x = a/10, y = 0;
    for (int i = 0; i < width; ++i) {
        y = y*10 + (x % 10);
        x /= 10;
    }
    return a*pow(10, width) + y;
}

bool isPrime(int x)
{
    if (x == 1)
        return false;
    if (x == 2)
        return true;
    if (x % 2 == 0)
        return false;

    for (int i = 3; i*i <= x; i += 2)
        if (x % i == 0)
            return false;
    return true;
}

昨天的AC代码:

/*
ID: chrt2001
PROG: pprime
LANG: C++
*/
#include <cstdio>
using namespace std;
int a, b;

void special()
{
    int A[3] = {5, 7, 11};
    for (int i = 0; i < 3; ++i)
        if (A[i] >= a && A[i] <= b)
            printf("%d\n", A[i]);
}

bool is_prime(int x)
{
    if (x == 1)
        return false;
    if (x % 2 == 0)
        return x == 2;
    for (int i = 3; i*i <= x; i += 2)
        if (x % i == 0)
            return false;
    return true;
}

int generate(int x, int y)
{
    int z = 0, w = 1;
    for (int i = x; i; i /= 10, w *= 10)
        z = z*10 + i%10;
    return x*w*10 + y*w + z;
}

int main()
{
    freopen("pprime.in", "r", stdin);
    freopen("pprime.out", "w", stdout);
    scanf("%d %d", &a, &b);
    special();
    for (int i = 1; ; ++i)
        for (int j = 0; j < 10; ++j) {
            int t = generate(i, j);
            if (t > b)
                return 0;
            if (t >= a && is_prime(t))
                printf("%d\n", t);
        }
    return 0;
}

看了ANALYSIS之后发现判素数那里把偶数的优化忘到了九霄云外……于是加上了,快了一点。后来发现2忘记特判了……虽然这里无妨。上面是修改过的。

代码风格有一些变化:以前主要用流进行IO,现在主要用C风格的库函数;以前前置函数声明,把定义写在后面,现在把定义写在前面,不声明;以前偏爱驼峰命名法,现在偏爱下划线命名法。

PROB Superprime Rib

求N位的superprime,即保留左边任意多位(大于0)仍是素数的素数。1<=N<=8。

x是superprime <=> x是素数且x/10是superprime。也许可以打一张素数表然后递推?数据范围太大……TEXT讲了位运算,要不我用bitset?也许可以打个几万的表,剩下的枚举?几万比较合适呢?枚举所有数?很明显偶数不用枚举,这样就只剩{1, 3, 5, 7, 9}。咦?好像可以暴力了。为什么偶数不用枚举?我们用十进制,它是10的因子。10还有另一个因子5,所以5也不用枚举。在一个superprime后面加{1, 3, 7, 9},判定是不是素数就好。

由于是逐层递推的,所以我用了BFS。其实Section 1.4的TEXT讲过这个问题,推荐的是DFS,ANALYSIS页面也用了DFS。其实写起来都很简单。

/*
ID: chrt2001
PROG: sprime
LANG: C++
*/
#include <cstdio>
#include <queue>
using namespace std;
const int d[4] = {1, 3, 7, 9};
int n;
struct Node {
    int x, w;
};
queue<Node> Q;

void init()
{
    int prime[4] = {2, 3, 5, 7};
    for (int i = 0; i < 4; ++i)
        Q.push((Node){prime[i], 1});
}

inline bool is_prime(int x)
{
    if (x == 1)
        return false;
    if (x % 2 == 0)
        return x == 2;
    for (int i = 3; i*i <= x; i += 2)
        if (x % i == 0)
            return false;
    return true;
}

void bfs()
{
    init();
    Node u;
    while (!Q.empty()) {
        u = Q.front();
        Q.pop();
        if (u.w == n) {
            printf("%d\n", u.x);
        } else
            for (int i = 0; i < 4; ++i) {
                int y = u.x*10 + d[i];
                if (is_prime(y))
                    Q.push((Node){y, u.w+1});
            }
    }
}

int main()
{
    freopen("sprime.in", "r", stdin);
    freopen("sprime.out", "w", stdout);
    scanf("%d", &n);
    bfs();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值