分类: 技术

  • NOIP2009 Hankson的趣味题 解题报告

    Hanks 博士是BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson 正在思考一个有趣的问题。
    今天在课堂上,老师讲解了如何求两个正整数c1 和c2 的最大公约数和最小公倍数。现在Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1,设某未知正整数x 满足:
    1. x 和a0 的最大公约数是a1;
    2. x 和b0 的最小公倍数是b1。
    Hankson 的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x 的个数。请你帮助他编程求解这个问题。

    第一行为一个正整数n,表示有n 组输入数据。接下来的n 行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证a0 能被a1 整除,b1 能被b0 整除。
    【数据范围】
    对于 50%的数据,保证有1≤a0,a1,b0,b1≤10000 且n≤100。
    对于 100%的数据,保证有1≤a0,a1,b0,b1≤2,000,000,000 且n≤2000。

    每组输入数据的输出结果占一行,为一个整数。
    对于每组数据:若不存在这样的 x,请输出0;
    【说明】
    第一组输入数据,x 可以是9、18、36、72、144、288,共有6 个。
    第二组输入数据,x 可以是48、1776,共有2 个。
    若存在这样的 x,请输出满足条件的x 的个数;

    ====================================华丽的分割线====================================

    题目如上,我一开始的思路是把所有a1的倍数都枚举一次,然后用b1作为上限。

    但是估计会超时,就没下手,网上查了一下,有人用我这个思路,50分!!!以后我还是有思路就尝试下吧。。
    后来看了另外的思路,任何数都能够表示成素数之和,如:5=5^1,, 6 = 2^13^1,, 8 = 2^3等等。
    然后是最大公倍数=2^min(x1, y1)
    3^min(x2, y2)……
    最小公倍数=2^max(x1, y1)
    3^max(x2, y2)*……
    代码等下写上来.

    代码如下:

    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #define bzero(a) memset(a, 0, sizeof(a))
    #define MAX 10000
    int prime[MAX], count[MAX], num[MAX];
    int tot, t;
    
    int hcf(int a, int b)
    {
     int t;
     while(b){
     t = b;
     b = a % t;
     a = t;
     }
     return a;
    }
    
    void dfs(int now, int sum)
    {
     int i, n;
     if(now == t){
     num[tot++] = sum;
     return ;
     }
     dfs(now + 1, sum);
     for(i = 0; i < count[now]; i++){
     sum *= prime[now];
     dfs(now + 1, sum);
     }
    }
    
    void work(int n)
    {
     int i = 2;
     int limit = sqrt(n);
     while(i <= limit){
     if(n % i == 0){
     prime[t] = i;
     count[t] = 0;
     do{
     count[t]++;
     n /= i;
     }while(n % i == 0);
     t++;
     limit = sqrt(n);
     }
     i++;
     }
     if(n != 1){
     prime[t] = n;
     count[t++] = 1;
     }
     dfs(0, 1);
    }
    
    int main(void)
    {
     int n;
     int a0, a1, b0, b1;
     int i, j;
     int ans;
     scanf(%d, &n);
     for(i = 0; i < n; i++){
     scanf(%d%d%d%d, &a0, &a1, &b0, &b1);
     bzero(num);
     bzero(count);
     bzero(prime);
     tot = t = 0;
     work(b1);
     ans = 0;
     for(j = 0; j < tot; j++){
     // 这里的变量我用的都是i...+_^
     if((hcf(num[j], a0) == a1) &&
     ((num[j] / hcf(num[j], b0) * b0) == b1)){
     //必须写成上面这样, 而不是 num[j] * b0 / hcf(num[j], b0).
     //找了我两个多小时, 原来是先相乘的话会超过int的范围, 
     //而先除的话就不会超范围了 
     ans++;
     }
     }
     printf(%d\\n, ans);
     }
     return 0;
    }
     忽然想到了剪枝地方法,直接寻找所有与b0的最小公倍数是b1的数字,代码如下:
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #define bzero(a) memset(a, 0, sizeof(a))
    #define MAX 10000
    int prime[MAX], count[MAX], num[MAX];
    int tot, t;
    int a0, a1, b0, b1;
    
    int hcf(int a, int b)
    {
     int t;
     while(b){
     t = b;
     b = a % t;
     a = t;
     }
     return a;
    }
    
    void dfs(int now, int sum)
    {
     int i, n, r;
     if(now == t){
     num[tot++] = sum;
     return ;
     }
     n = 0;
     r = prime[now];
     while(b0 % (r) == 0 && n < count[now]){ //剪枝, 使程序更上一层楼
     //这里直接寻找和b0的公约数是b1的数字 
     r *= prime[now];
     n++;
     }
     if(n < count[now]){
     while(n + 1 < count[now]){
     r *= prime[now];
     n++;
     }
     dfs(now + 1, sum * r);
     return ;
     }
    
     dfs(now + 1, sum);
     for(i = 0; i < count[now]; i++){
     sum *= prime[now];
     dfs(now + 1, sum);
     }
    }
    
    void work(int n)
    {
     int i = 2;
     int limit = sqrt(n);
     while(i <= limit){
     if(n % i == 0){
     prime[t] = i;
     count[t] = 0;
     do{
     count[t]++;
     n /= i;
     }while(n % i == 0);
     t++;
     limit = sqrt(n);
     }
     i++;
     }
     if(n != 1){
     prime[t] = n;
     count[t++] = 1;
     }
     dfs(0, 1);
    }
    
    int main(void)
    {
     int n;
     int i, j;
     int ans;
     scanf(%d, &n);
     for(i = 0; i < n; i++){
     scanf(%d%d%d%d, &a0, &a1, &b0, &b1);
     bzero(num);
     bzero(count);
     bzero(prime);
     tot = t = 0;
     work(b1);
     ans = 0;
     for(j = 0; j < tot; j++){
     // 这里的变量我用的都是i...+_^
    /* if((hcf(num[j], a0) == a1) &&
     ((num[j] / hcf(num[j], b0) * b0) == b1)){*/
     //必须写成上面这样, 而不是 num[j] * b0 / hcf(num[j], b0).
     //找了我两个多小时, 原来是先相乘的话会超过int的范围, 
     //而先除的话就不会超范围了
    
     //当程序优化之后上面的判断就变成了下面的判断:
     if(hcf(num[j], a0) == a1){
     ans++;
     }
     }
     printf(%d\\n, ans);
     }
     return 0;
    }

    忽然又想到了一些剪枝,我还尝试一下。

  • NOIP2009 潜伏者 解题报告

    今天把NOIP 2009的所有题目做了一遍,当做考试来做,结果只做出了这一题,而且还只有90分。。死心了。。
    代码如下:

    /*要注意:
     * 1.\'A\'-\'Z\'扫描完毕就停止, (不管内容还有没有~!)
     * 2,长度小于26就Failed
     * 3,二者是一一对应的关系
     * 4,输入的字符串是密子, 不是原信息
     */
    #include <stdio.h>
    **char** map[26], mapp[26];
    **char** string[101];
    **char** start[101], end[101];
    
    **int** main(**void**)
    {
     **int** count = 0;
     **int** i;
     scanf(%s, end); //输入的时候写成了%s\\n,,
     scanf(%s, start);
     scanf(%s, string);
     i = 0;
     **while**(count < 26 && end[i] != \'\\0\'){
     **if**(((map[end[i] - \'A\'] > 0) && (map[end[i] - \'A\'] != start[i])) ||
     ((mapp[start[i] - \'A\'] > 0) && (map[start[i] - \'A\'] != end[i]))){
     //两种数据是一一对应的~! 
     **break**;
     }
     **if**(map[end[i] - \'A\'] == 0){ //忘记给count递增了..
     map[end[i] - \'A\'] = start[i];
     mapp[start[i] - \'A\'] = end[i];
     //两种数据是一一对应的~!
     count++;
     }
     i++; //掉了i++
     //差点提交了, 要把i放在判断的外面才行
     }
     **if**(count != 26){
     printf(Failed\\n);
     **return** 0;
     }
     i = 0; //忘记赋值了
     **while**(string[i] != \'\\0\'){
     putchar(map[string[i] - \'A\']);
     i++; //掉了i++
     }
     putchar(\'\\n\');
     **return** 0;
    }

    顺便提一下,上面的代码是使用Vim转换的!

  • 中缀表达式转换后缀表达式

    能够把中缀表达式转换成后缀表达式,是在为http://www.rqnoj.cn/Problem_18.html 这一题做准备,感觉写的还不错,关键是在优先级的处理方面(compare函数),整个代码如下(包含驱动函数main):
    只支持+-*^()和变量

    #include <stdio.h>
    #include <ctype.h>
    #define MAX 101
    #define NUM 0
    #define CHAR 1
    #define OPER 2
    #define STR +-*^()
    const unsigned used = 100000000;
    #define bits 8
    struct t{
     unsigned num[MAX];
     char type[MAX];
     int len;
    }std;
    unsigned stack[50];
    int top;
    #define push(a) ({stack[top++] = a;})
    #define pop() ({stack[--top];})
    
    void add(struct t *to, unsigned key, char type)
    {
     to->num[to->len] = key;
     to->type[to->len] = type;
     to->len++;
    }
    
    int par[6][6] ={{-1, -1, 1, 1, 1, -1},
     {-1, -1, 1, 1, 1, -1},
     {-1, -1, -1, 1, 1, -1},
     {-1, -1, -1, -1, 1, -1},
     {1, 1, 1, 1, 1, 0},
     {-1, -1, -1, -1, 0, 1},};
    /*
     0:加法
     1:减法
     2:乘法
     3:求幂
     4:正括号
     5:反括号
    */
    
    int compare(int a, int b)
    {
     char *i, *j;
     i = strchr(STR, a);
     j = strchr(STR, b);
     a = i - STR;
     b = j - STR;
     return par[a][b];
    }
    
    void change(struct t *to, char *from)
    {
     int i, j;
     int ch;
     unsigned t;
     push(\'(\');
     for(i = 0; from[i] != \'\\0\'; i++){
     ch = from[i];
     if(strchr(STR, ch) != NULL){ /* 处理三种情况 */
     j = compare(stack[top - 1], ch);
     while(j < 0){
     add(to, pop(), CHAR);
     j = compare(stack[top - 1], ch);
     }
     if(j == 0){
     pop();
     }else{
     push(ch);
     }
     }else if(isdigit(ch)){
     t = 0;
     while(isdigit(from[i])){
     t *= 10;
     t += from[i++] - \'0\';
     }
     i--;
     add(to, t, NUM);
     }else if(ch == \'a\'){
     add(to, ch, OPER);
     }
     }
     while(top != 1){ /* 把最后几个符号去掉, 但是在函数
     开始加入的正括号不加入 */
     add(to, pop(), CHAR);
     }
    }
    
    int main(void)
    {
     int i;
     char tmp[51];
     scanf(%s, tmp);
     change(&std, tmp);
     for(i = 0; i < std.len; i++){
     if(i != 0){
     printf( );
     }
     switch(std.type[i]){
     case NUM:
     printf(%u, std.num[i]);
     break;
     case CHAR:
     printf(%c, std.num[i]);
     break;
     case OPER:
     printf(%c, std.num[i]);
     break;
     }
     }
     printf(\\n);
     return 0;
    }
  • USACO 3.3-1 Riding the Fences骑马修栅栏

    欧拉回路,我知道怎么做,但是我不知到为什么可以这么做!,囧囧;就当是背课文把,反正这就是欧拉回路。
    如果有节点的度为奇数就从它开始,否则就从最小的开始,然后就是看下面的代码把:

     #include <stdio.h>
    #define MAXV 500
    #define MAXE 1024
    char map[MAXV][MAXV];
    int path[MAXE];
    int degree[MAXV];
    int len;
    int max;
    
    void add(int a, int b)
    {
     map[a][b]++, degree[a]++;
    }
    
    void delete(int a, int b)
    {
     map[a][b]--, degree[b]--;
    }
    
    int getneighbor(int a)
    {
     int i;
     for(i = 0; degree[a] != 0; i++){
     if(map[i][a]){
     return i;
     }
     }
    }
    
    void fence(int now)
    {
     int i;
     while(degree[now]){
     i = getneighbor(now);
     delete(i, now);
     delete(now, i);
     fence(i);
     }
     path[len++] = now;
    }
    
    int main(void)
    {
     int i;
     int n, k = 501;
     int a, b;
     freopen(fence.in, r, stdin);
     freopen(fence.out, w, stdout);
     scanf(%d, &n);
     for(i = 0; i < n; i++){
     scanf(%d%d, &a, &b);
     a--, b--;
     add(a, b);
     add(b, a);
     if(max < a){
     max = a;
     }
     if(max < b){
     max = b;
     }
     if(k > a){
     k = a;
     }
     if(k > b){
     k = b;
     }
     }
     for(i = 0; i <= max; i++){
     if(degree[i] & 1){
     k = i;
     break;
     }
     }
     fence(k);
     for(i = len - 1; i >= 0; i--){
     printf(%d\\n, path[i] + 1);
     }
     return 0;
    }
  • 算法导论 习题之插入排序从大到小

    插入排序其实很好理解,就是保证前i – 1个都是排好序了的,再排第i个,下面这个是从大到小排序的:

    
    /* 从大到小的插入排序 */
    void insert_sort(int a[], int n)
    {
     int i, j;
     int key;
     for(i = 1; i < n; i++){
     key = a[i];
     for(j = i - 1; (j >= 0) && (key > a[j]); j--){
     a[j + 1] = a[j];
     }
     a[j + 1] = key;
     }
    }
    
    /* 驱动程序如下 */
    int main(void)
    {
     int a[10];
     int i;
     for(i = 0; i < 10; i++){
     scanf(%d, &a[i]);
     }
     insert_sort(a, 10);
     for(i = 0; i < 10; i++){
     printf(%d , a[i]);
     }
     return 0;
    }
  • NOIP2005 循环 解题报告

    刚看到题目,我不知所措,真不知道怎么做(NOIP我真的是太菜了。)。
    到网上找到了一个高手的结题报告(几个题目我都是搜到的他的。),原来可以用DP来解大致的思路是这样子的:
    比如这里有一个循环(只是假设,可能没有这个数):123 245 344 123,这就是一个循环数,没错吧?,好,再仔细观察一下,第一项和最后一项的后二位数也是相同的,也就是说后k个数循环的长度是后k-1个数循环长度的倍数。
    恩,至于代码的话,等下再发上来

    (2010年8月4日18:26:10),这一题希望能成为我的OI转折题,没什么别的,这题我吃了不少亏,总结了几条教训,如下:
    1、拿到题目先仔细分析,是否拥有子问题。
    上面这一点不只是DP,各种算法都是大事化小,小事化了,不说用什么出众的数学,但是分析题目的时候,没有别的,要理性,争取能够找到更小的问题,然后逐一攻破。
    2、不要为了一些微不足道剪纸而丧失了程序的正确性。
    总是认为程序的效率很重要,要多用什么位运算,一些小型剪枝。以后的话尽量不要用这些运算,因为Noip只是要确保在规定时间内完成程序,所以100ms和10ms对于成绩来说都是一样的。
    3、暂时就说这么多吧。
    当然,程序AC之后还是可以适当的优化一下,体验速度给我带来的快乐!
    题目的代码如下,可读性自我感觉4颗星!

    
    #include <stdio.h>
    #include <string.h>
    #define max(a, b) ((a)>(b)?(a):(b))
    #define read(a) read_(&a)
    #define output(a) output_(&a)
    #define mul(a, b) mul_(&a, &b)
    #define mulnum(a, b) mulnum_(&a, b)
    #define copy(a, b) memcpy((a), (b), sizeof(numtype) * ((b)->len) + sizeof(int))
    #define give(a, b) give_(&a, b)
    #define getnum(a, i) (a.num[i])
    #define MAX 201
    const int used = 10;
    typedef char numtype;
    typedef struct bignum{
     int len;
     numtype num[MAX];
    }bignum;
    bignum std, x, num, num1, ans;
    int k;
    
    void read_(bignum *a)
    {
     char tmp[101];
     int i;
     scanf(%s, tmp);
     a->len = strlen(tmp);
     i = a->len - 1;
     while(i >= 0){
     a->num[i] = tmp[a->len - 1 - i] - \'0\';
     i--;
     }
    }
    
    void output_(bignum *a)
    {
     int i;
     numtype *num = a->num;
     for(i = a->len - 1; i >= 0; i--){
     printf(%d, num[i]);
     }
     printf(\\n);
    }
    
    void give_(bignum *a, int n)
    {
     int i;
     for(i = 0; n != 0; i++){
     a->num[i] = n % used;
     n /= used;
     }
     a->len = i;
    }
    
    void mul_(bignum *a, bignum *b)
    {
     static bignum tmp;
     int len = a->len + b->len - 1;
     int i, j;
     numtype re;
     memset(&tmp, 0, sizeof(bignum));
     /* 上面这段代码我错了一次二次三次.. 我最后才知道所有的问题都是它... */
     for(i = 0; i < a->len; i++){
     re = 0;
     for(j = 0; j < b->len; j++){
     tmp.num[i + j] += a->num[i] * b->num[j] + re;
     //是+=
     re = tmp.num[i + j] / used;
     if(re > 0){
     tmp.num[i + j] %= used;
     }
     }
     if(re > 0){
     tmp.num[i + j] = re;
     len = max(i + j + 1, len);
     }
     }
     if(len > k){
     len = k;
     }
     tmp.len = len;
     copy(a, &tmp);
    }
    
    void mulnum_(bignum *a, int n)
    {
     int i;
     numtype re = 0;
     /* 忘记初始化 */
     for(i = 0; i < a->len; i++){
     a->num[i] = a->num[i] * n + re;
     re = a->num[i] / used;
     if(re > 0){
     a->num[i] %= used;
     }
     }
     if(re > 0){
     a->num[i] = re;
     a->len++;
     } 
    }
    
    int main(void)
    {
     int i, j;
     int a, b;
     read(std);
     give(ans, 1);
     copy(&x, &std);
     give(ans, 1);
     scanf(%d, &k);
     for(i = 0; i < k; i++){
     copy(&num, &std);
     give(num1, 1);
     b = getnum(std, i);
     for(j = 1; j <= 10; j++){
     mul(num1, x);
     mul(num, x);
     a = getnum(num, i);
     if(a == b){
     copy(&x, &num1);
    /* give(t, j);
     mul(ans, t);*/
     mulnum(ans, j);
     break;
     }
     }
     if(j > 10){
     printf(-1\\n);
     return 0;
     }
     }
     output(ans);
     return 0;
    }
  • Glibc 中的 qsort

    这函数真够长的,吓死!
    记得我前两天看到别人做的一个程序,用的是自己写的快速排序,而我用的qosrt,结果我的100ms,他的350ms。这程序真贼快的
    因为我把每天的倾城都安排好了,所以只有半个小时的时间看,所以没看完,先注释一部分吧,明天再补一部分,估计一下子还看不完。整个qsort的代码如下:

    
    #include <alloca.h>
    #include <limits.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* 交换一块内存数据, 长度为size字节 */
    #define SWAP(a, b, size) \\
     do \\
     { \\
     register size_t __size = (size); \\
     register char *__a = (a), *__b = (b); \\
     do \\
     { \\
     char __tmp = *__a; \\
     *__a++ = *__b; \\
     *__b++ = __tmp; \\
     } while (--__size > 0); \\
     } while (0)
    
    #define MAX_THRESH 4
    
    typedef struct{
     char *lo;
     char *hi;
    }stack_node;
    /* 初步猜测lo和hi,的功能,根据下面的PUSH和POP宏, 估计是一段内存的两端
     lo就是low, hi就是high,就是说从low所指向的位置到hi所指向的位置就是
     这个元素的内容。 */
    
    #define STACK_SIZE (CHAR_BIT * sizeof(size_t))
     /* 关于这个CHAR_BIT在网上搜了下,在limits.h文件中有定义:
     #define CHAR_BIT 8 */
    #define PUSH(low, high) ((void)((top->lo = (low)), (top->hi = (high)), ++top))
    #define POP(low, high) ((void)(--top, (low = top->lo), (high = top->hi)))
     /* 译(准确的说是注释的人:zqynux/My S-K-Y)者感到有点奇怪,
     能转换void这个类型 */
    #define STACK_NOT_EMPTY (stack < top)
    /* 上面三个都是超快速 + 简单的栈操作 */
    
    void _quicksort (void *const pbase, size_t total_elems, size_t size,
     __compar_d_fn_t cmp, void *arg)
     /* 其实__compar_d_fn_t这个类型我没查也知道是什么类型, 就是一个函数指针,
     所以就没查了. */
    {
     register char *base_ptr = (char *) pbase;
     const size_t max_thresh = MAX_THRESH * size;
    
     if(total_elems == 0)
     /* Avoid lossage with unsigned arithmetic below. */
     return;
    
     if(total_elems > MAX_THRESH){
     char *lo = base_ptr;
     char *hi = &lo[size * (total_elems - 1)];
     stack_node stack[STACK_SIZE];
     stack_node *top = stack;
    
     PUSH (NULL, NULL);
    
     while (STACK_NOT_EMPTY){
     char *left_ptr;
     char *right_ptr;
    
     /* Select median value from among LO, MID, and HI. Rearrange
     LO and HI so the three values are sorted. This lowers the
     probability of picking a pathological pivot value and
     skips a comparison for both the LEFT_PTR and RIGHT_PTR in
     the while loops. */
    
     char *mid = lo + size * ((hi - lo) / size >> 1);
    
     if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
     SWAP (mid, lo, size);
     if ((*cmp) ((void *) hi, (void *) mid, arg) < 0)
     SWAP (mid, hi, size);
     else
     goto jump_over;
     if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
     SWAP (mid, lo, size);
    jump_over: ;
    
     left_ptr = lo + size;
     right_ptr = hi - size;
    
     /* Here\'s the famous ``collapse the walls\'\' section of quicksort.
     Gotta like those tight inner loops! They are the main reason
     that this algorithm runs much faster than others. */
     do{
     while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
     left_ptr += size;
    
     while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
     right_ptr -= size;
    
     if (left_ptr < right_ptr){
     SWAP (left_ptr, right_ptr, size);
     if (mid == left_ptr)
     mid = right_ptr;
     else if (mid == right_ptr)
     mid = left_ptr;
     left_ptr += size;
     right_ptr -= size;
     }else if (left_ptr == right_ptr){
     left_ptr += size;
     right_ptr -= size;
     break;
     }
     }while (left_ptr <= right_ptr);
    
     /* Set up pointers for next iteration. First determine whether
     left and right partitions are below the threshold size. If so,
     ignore one or both. Otherwise, push the larger partition\'s
     bounds on the stack and continue sorting the smaller one. */
    
     if ((size_t) (right_ptr - lo) <= max_thresh){
     if ((size_t) (hi - left_ptr) <= max_thresh)
     /* Ignore both small partitions. */
     POP (lo, hi);
     else
     /* Ignore small left partition. */
     lo = left_ptr;
     }else if ((size_t) (hi - left_ptr) <= max_thresh)
     /* Ignore small right partition. */
     hi = right_ptr;
     else if ((right_ptr - lo) > (hi - left_ptr)){
     /* Push larger left partition indices. */
     PUSH (lo, right_ptr);
     lo = left_ptr;
     }else{
     /* Push larger right partition indices. */
     PUSH (left_ptr, hi);
     hi = right_ptr;
     }
     }
     }
    
     /* Once the BASE_PTR array is partially sorted by quicksort the rest
     is completely sorted using insertion sort, since this is efficient
     for partitions below MAX_THRESH size. BASE_PTR points to the beginning
     of the array to sort, and END_PTR points at the very last element in
     the array (*not* one beyond it!). */
    
    #define min(x, y) ((x) < (y) ? (x) : (y))
    
     {
     char *const end_ptr = &base_ptr[size * (total_elems - 1)];
     char *tmp_ptr = base_ptr;
     char *thresh = min(end_ptr, base_ptr + max_thresh);
     register char *run_ptr;
    
     /* Find smallest element in first threshold and place it at the
     array\'s beginning. This is the smallest array element,
     and the operation speeds up insertion sort\'s inner loop. */
    
     for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
     if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
     tmp_ptr = run_ptr;
    
     if (tmp_ptr != base_ptr)
     SWAP (tmp_ptr, base_ptr, size);
    
     /* Insertion sort, running from left-hand-side up to right-hand-side. */
    
     run_ptr = base_ptr + size;
     while ((run_ptr += size) <= end_ptr){
     tmp_ptr = run_ptr - size;
     while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
     tmp_ptr -= size;
    
     tmp_ptr += size;
     if (tmp_ptr != run_ptr){
     char *trav;
    
     trav = run_ptr + size;
     while (--trav >= run_ptr){
     char c = *trav;
     char *hi, *lo;
    
     for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
     *hi = *lo;
     *hi = c;
     }
     }
     }
     }
    }
  • NOIP 过河 解题报告

    这题我不怎么说吧,我在网上搜的,到现在为止为什么能这样我还是没想通,只知道这样能过。
    就当是个定理吧,记住就是了,这题我不太想解释,看代码吧:

    #include <stdio.h>
    #define INT_MAX 200000000
    int stone[100];
    int map[9190], f[9190];
    
    int com(const void *a, const void *b){ return *(int *)a - *(int *)b; }
    
    int main(void)
    {
     int i, j, k;
     int l, s, t, n;
     int p, jmp = 0;
     int ans = 0;
    
     scanf(%d%d%d%d, &l, &s, &t, &n);
     for(i = 0; i <= 9190; i++){
     f[i] = INT_MAX;
     }
     f[0] = 0;
     for(i = 0; i < n; i++){
     scanf(%d, &stone[i]);
     }
     if(s == t){
     for(i = 0; i < n; i++){
     if(stone[i] % s == 0){
     ans++;
     }
     }
     printf(%d\\n, ans);
     return 0;
     }
     qsort(stone, n, sizeof(int), com);
     k = 0;
     for(i = 0; i < n; i++){
     p = stone[i] - k - 1;
     if(p >= s * t){
     jmp += p - s * t;
     }
     map[stone[i] - jmp] = 1;
     k = stone[i];
     }
     if(l - k > s*t){
     jmp += l - k - s*t;
     }
     l -= jmp;
     for(i = 0; i <= l; i++){
     if(f[i] == INT_MAX){
     continue;
     }
     for(j = s; j <= t; j++){
     if(f[i + j] > f[i] + map[i + j]){
     f[i + j] = f[i] + map[i + j];
     }
     }
     }
     for(i = l, ans = INT_MAX; i <= l + t - 1; i++){
     if(ans > f[i]){
     ans = f[i];
     }
     }
     printf(%d\\n, ans);
    
     return 0;
    }
  • Glibc 中的 memset

    Glibc的效率真的快!快到让我想不到!!!
    这个memset跑的贼快~!不过我还是没想通,为什么不用汇编呢?sep movw,速度可能更快!
    对memset的注释如下:

    C语言: [Codee#12483](http://fayaa.com/code/view/12483/)
    
    void *memset(void *dstpp, int c, size_t len)
    {
     long int dstp = (long int) dstpp;
     /* 正在翻阅为什么用long int 而不是void *. */
     /* 被高手指点了一下,不能用void *,因为它不能增,只能用char *. */
    
     /* 还有一位仙人告诉我, 这里用long int 纯粹只是个人喜好, 另见FreeBSD的代码:
    
     [http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/string/memset.c?rev=1.9](http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/string/memset.c?rev=1.9) */
    
     if (len >= 8) {
     size_t xlen;
     op_t cccc;
     /* 关于op_t这个类型, 在memcmp中有定义
     # define op_t unsigned long int
     # define OPSIZ (sizeof(op_t)) */
    
     /* 每次操作对多个字节进行赋值 */
     cccc = (unsigned char) c;
     cccc |= cccc << 8;
     cccc |= cccc << 16;
     if (OPSIZ > 4) /* 如果是64位的机子的话, 还可以再多用4个字节 */
     cccc |= (cccc << 16) << 16;
    
     while (dstp % OPSIZ != 0) { /* 让内存地址对齐地址总线的长度, 以
     得到最高的效率! */
     ((byte *) dstp)[0] = c;
     dstp += 1;
     len -= 1;
     }
    
     xlen = len / (OPSIZ * 8); /* 每次赋值8个,也就是说如果在32位的机子上的话
     每次赋值4*8=32个字节,64位的机子就是64字节 */
     while (xlen > 0) {
     ((op_t *) dstp)[0] = cccc;
     ((op_t *) dstp)[1] = cccc;
     ((op_t *) dstp)[2] = cccc;
     ((op_t *) dstp)[3] = cccc;
     ((op_t *) dstp)[4] = cccc;
     ((op_t *) dstp)[5] = cccc;
     ((op_t *) dstp)[6] = cccc;
     ((op_t *) dstp)[7] = cccc;
     dstp += 8 * OPSIZ;
     xlen -= 1;
     }
     len %= OPSIZ * 8; /* 得到余下的未赋值的字节数,一定小于32 */
    
     xlen = len / OPSIZ; /* 每次赋值1个, 32位机子4个字节一次, 64位8个 */
     while (xlen > 0) {
     ((op_t *) dstp)[0] = cccc;
     dstp += OPSIZ;
     xlen -= 1;
     }
     len %= OPSIZ; /* 得到余下的未赋值的字节书,一定小于4 */
     }
    
     while (len > 0) { /* 最后剩下的几个字节单独赋值 */
     ((byte *) dstp)[0] = c;
     dstp += 1;
     len -= 1;
     }
    
     return dstpp;
    }
  • USACO 3.2.6 Sweet Butter 香甜的黄油 解题报告

    这题一开始我用的那个O(n^3)的算法,叫什么名字我忘了,肯定是超时了咯,因为图是稀疏图,这样是肯定超时的,后来就看标称,看了好久,真没想到标称竟然如此精妙,里面设计了一个能够以O(1)的速度查找元素的功能,整个程序是执行了至多n次Dijkstra算法,然后我在里面增加了一些优化(几乎和时间无关的优化,因为数据量太大,CPU又太好用了),比如用位运算代替乘除法(乘以或除以2),在上滤和下滤的过程把递归改成了非递归,防止重复的牛所在的牧场进行计算,让heap_val在上滤和下滤的过程中不修改,反正最大的数据程序还是要0.16s才能完成,不知道算不算快。
    不过此代码是我写的可读性最低的代码之一,能读都就读吧。

    /*
    LANG: C
    ID: zqynux2
    PROG: butter
    */
    #include <stdio.h>
    const int INTMAX = 1<<30;
    
    int heap_val[800];
    int heap_id[800];
    int heap_lookup[800];
    int heap_size;
    #define left(i) (((i) << 1) + 1)
    #define right(i) (((i) << 1) + 2)
    #define parent(i) ((i - 1) >> 1) 
    
    void heapdown(int t)
    {
     int i, ch;
     int id = heap_id[t];
     for(i = t; left(i) < heap_size; i = ch){
     ch = left(i);
     if(ch + 1 < heap_size && heap_val[heap_id[ch + 1]] <
     heap_val[heap_id[ch]]){
     ch++;
     }
     if(heap_val[id] > heap_val[heap_id[ch]]){
     heap_lookup[heap_id[ch]] = i;
     heap_id[i] = heap_id[ch];
     }else{
     break;
     }
     }
     heap_lookup[id] = i;
     heap_id[i] = id;
    }
    
    void heapup(int t)
    {
     int i;
     int id = heap_id[t];
     for(i = t; i > 0 && heap_val[id]
     < heap_val[heap_id[parent(i)]]; i = parent(i)){
     heap_lookup[heap_id[parent(i)]] = i;
     heap_id[i] = heap_id[parent(i)];
     }
     heap_lookup[id] = i;
     heap_id[i] = id;
    }
    
    int cows[500];
    int link[800][800];
    int value[800][800];
    int count[800];
    int used[500];
    int fixed[800];
    int dist[800][800];
    int n, p, c;
    
    int main(void)
    {
     int i, j, k;
     int t, s;
     freopen(butter.in, r, stdin);
     freopen(butter.out, w, stdout);
     scanf(%d%d%d, &n, &p, &c);
     for(i = 0; i < n; i++){
     scanf(%d, &cows[i]);
     cows[i]--; //掉了这一行代码 
     }
     {
     int a, b, d;
     for(i = 0; i < c; i++){
     scanf(%d%d%d, &a, &b, &d);
     a--, b--;
     link[a][count[a]] = b;
     link[b][count[b]] = a;
     value[a][count[a]] = value[b][count[b]] = d;
     count[a]++, count[b]++;
     }
     }
     for(i = 0; i < n; i++){
     if(used[i]){
     continue;
     }
     used[i] = 1;
     heap_size = p;
     for(j = 0; j < p; j++){
     heap_id[j] = j;
     heap_val[j] = INTMAX;
     heap_lookup[j] = j;
     }
     heap_val[cows[i]] = 0;
     heapup(cows[i]);
     memset(fixed, 0, sizeof(fixed));
     while(heap_size != 0){
     t = heap_id[0];
     dist[cows[i]][t] = heap_val[t];
     fixed[t] = 1;
     heap_size--;
     heap_lookup[heap_id[heap_size]] = 0;
     heap_id[0] = heap_id[heap_size];
     heapdown(0);
     for(k = 0; k < count[t]; k++){
     s = link[t][k];
     if(!fixed[s] && heap_val[s] > heap_val[t] + value[t][k]){
     heap_val[s] = heap_val[t] + value[t][k];
     heapup(heap_lookup[s]);
     }
     }
     }
     }
     {
     int ans = INTMAX, tmp;
     for(i = 0; i < p; i++){
     tmp = 0;
     for(j = 0; j < n; j++){
     tmp += dist[cows[j]][i];
     }
     if(ans > tmp){
     ans = tmp;
     }
     }
     printf(%d\\n, ans);
     }
     return 0;
    }