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;
}