博客

  • 能量项链 解题报告

    额,花了我三四个小时的时间,终于知道了,是动态规划的题目(我以前做过,不过那个时候真是不知不觉过去的。),我以前只做过背包的题目,这个题目是这暑假以来觉得最复杂的题目了,因为非动态规划的代码一看就能看懂,动态规划的,方程没想通你就看不懂代码!
    我的方程是:f[i][j] = max(f[i][k – i] + f[k][j – k + i] + boll[i] boll[i + j] boll[k])); 其中f[i][j]的意思是从i开始j个珠子的最大能量数,boll[i]是下标为i的珠子的能量。然后就是要注意一下细节,有人的实现方法是使用了长度为200的数组,有人是使用了mod;在这里我是用的后者,用了mod,牺牲了效率,节省了空间。
    这题确实要做下纪念,我的算法水平从今天开始会向新的高度上升,我会站在世界的顶点的!

    C语言:

    #include <stdio.h>
    #define max(a, b) ((a)>(b)?(a):(b))
    unsigned f[100][101];
    int boll[100];
    int n;
    
    int main(void)
    {
     int i, j, k;
     unsigned max = 0;
     scanf(%d, &n);
     for(i = 0; i < n; i++){
     scanf(%d, &boll[i]);
     }
    
     for(j = 2; j <= n; j++){
     for(i = 0; i < n; i++){
     for(k = i + 1; k < i + j; k++){
     f[i][j] = max(f[i][j], f[i][k - i] +
     f[k % n][j - k + i] +
     boll[i] * boll[(i + j) % n] *
     boll[k % n]);
     }
     }
     }
    
     for(i = 0; i < n; i++){
     if(max < f[i][n]){
     max = f[i][n];
     }
     }
    
     printf(%u\\n, max);
     return 0;
    }
  • 数列 结题报告

    这一题先把顺序仔细观察一下,可以发现顺序就是n^0, n^1, n^0 + n^1, n ^ 2, n ^ 0 + n ^2….之类的数据,仔细观察能够发现顺序就是先输出n^i次方,再把所有的n^0 ~ n^i-1 和n^i进行相加。
    嘿嘿,这题我倒是很自豪,发现了数学方法,仔细观察下,就可以发现第m个数字就是,n ^ log 2 (m) + n ^ log 2(m – 2 ^ log 2 (m)) + ….直到n^0为止,嘿嘿,代码如下:

    C语言:

    #include <stdio.h>
    #include <math.h>
    
    int pow_(int num, int x)
    {
     if(x == 0){
     return 1;
     }
     if(x == 1){
     return num;
     }
     if(x & 1){
     return pow_(num * num, x / 2) * num;
     }else{
     return pow_(num * num, x / 2);
     }
    }
    
    int main(void)
    {
     unsigned ans = 0;
     int i, t;
     int m, n;
     scanf(%d%d, &m, &n);
     while(n != 0){
     t = log2(n);
     ans += pow_(m, t);
     n -= pow_(2, t);
     }
     printf(%u\\n, ans);
     return 0;
    }
    

    这题有一个更好的解法,思路是完全的,但是不需要使用log2函数,直接使用位运算,感谢七妹的代码!

    #include <stdio.h>
    
    **int** main(**void**)
    {
     **unsigned** i, j;
     **unsigned** k, n;
     **unsigned** ans = 0, t;
     scanf(%u%u, &k, &n);
     t = 1;
     /* 注意, 从1开始 */
     **while**(n){
     **if**(n & 1){
     ans += t;
     }
     n >>= 1;
     t *= k;
     }
     printf(%d\\n, ans);
     **return** 0;
    }
  • Jam的计数法 解题报告

    这题我用的深搜,写来写去都觉得特别麻烦,干脆把从a到b的所有可能都计算出来,然后判断是否比输入的大,是就输出,让计数器加一,到五就退出程序,等下还优化试下,先把这个的代码贴上来。

    C语言:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int a, b;
    int n;
    char tmp[27];
    char str[27];
    int count;
    
    void srch(int now)
    {
     int i;
     if(now == n + 1){
     if(strcmp(tmp + 1, str + 1) > 0){
     count++;
     printf(%s\\n, tmp + 1);
     if(count == 5){
     exit(0);
     }
     }
     return ;
     }
     for(i = tmp[now - 1] + 1; i <= b; i++){
     tmp[now] = i;
     srch(now + 1);
     }
    }
    
    int main(void)
    {
     int i;
     scanf(%d%d%d\\n, &a, &b, &n);
     a += \'a\' - 1;
     b += \'a\' - 1;
     scanf(%s, str + 1);
     strcpy(tmp + 1, str + 1);
     *tmp = a - 1;
     srch(1);
     return 0;
    }

    它有可修改的余地,我还想想。
    把开始的时候处理一下,速度会明显快些,代码如下:
    C语言: Jam的计数法 改进版.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int a, b;
    int n;
    char tmp[26];
    char str[26];
    int count;
    
    void srch(int now, int start)
    {
     int i;
     if(now == n){
     if(strcmp(tmp, str) > 0){
     count++;
     printf(%s\\n, tmp);
     if(count == 5){
     exit(0);
     }
     }
     return ;
     }
     for(i = start; i <= b; i++){
     tmp[now] = i;
     srch(now + 1, i + 1);
     }
    }
    
    int main(void)
    {
     int i;
     scanf(%d%d%d\\n, &a, &b, &n);
     a += \'a\' - 1;
     b += \'a\' - 1;
     scanf(%s, str);
     srch(0, a);
     return 0;
    }
  • 开心的金明 解题报告

    这题可以说就是01背包的例题,我没什么多余的解释,上代码:

    C语言:

    
    #include <stdio.h>
    #define max(a, b) ((a)>(b)?(a):(b))
    int f[30000];
    
    int main(void)
    {
     int n, m;
     int i, j;
     int a, b;
     scanf(%d%d, &n, &m);
     for(i = 0; i < m; i++){
     scanf(%d%d, &a, &b);
     for(j = n; j >= a; j--){
     f[j] = max(f[j], f[j - a] + a * b);
     }
     }
     printf(%d\\n, f[n]);
     return 0;
    }
  • 明明的随机数 解题报告

    这题不难,不过我看到大部分的人都是使用的先用快排再去重,有些人是先去重再用快排,我这里就用的是哈希排序,能够以线性时间(O(n))对所有数据实现排序和去重。
    没什么好解释的,1~1000,把对应的数字放到相应的数组中就可以了。

    #include <stdio.h>
    char bucket[1001];
    
    int main(void)
    {
     int i, t;
     int n, ans = 0;
     scanf(%d, &n);
     for(i = 0; i < n; i++){
     scanf(%d, &t);
     if(!bucket[t]){
     ans++;
     }
     bucket[t] = 1;
     }
     printf(%d\\n, ans);
     t = 0; //判断空格
     for(i = 1; i <= 1000; i++){
     if(bucket[i]){
     if(t){
     printf( );
     }
     t = 1;
     printf(%d, i);
     }
     }
     printf(\\n);
     return 0;
    }
  • NOIP_2002.PJ1:级数求和 解题报告

    这题网上没找到较好的算法(数学方法),只好自己写暴力版本的了。。。这题真的不好做什么报告,提交就是。

    #include <stdio.h>
    
    int main(void)
    {
     double n;
     int i;
     double ans = 0;
     scanf(%lf, &n);
     for(i = 1; ans <= n; i++){
     ans += (double)1.0 / i;
     }
     printf(%d\\n, i - 1);
     return 0;
    }
  • NOIP_2001.PJ2:最大公约数与最小公倍数问题 解题报告

    看到这个题目,不知道怎么做,到网上搜了下最小公倍数和最大公约数之间的公式,就找到了思路,p q = x0 y0..那么我就枚举所有的p,然后求出q,再判断之间的最大公约数是不是x0,就Ok了。

    #include <stdio.h>
    
    int gcd(int x, int y)
    {
     int t;
     while(y > 0){
     t = x % y;
     x = y;
     y = t;
     }
     return x;
    }
    
    int main(void)
    {
     int i, ans = 0; //忘记初始化了... 
     int x, y, t, s;
     scanf(%d%d, &x, &y);
     t = x * y;
     for(i = x; i <= y; i += x){
     s = t / i;
     if((s * i == t) && (gcd(s, i) == x)){
     ans++;
     }
     }
     printf(%d\\n, ans);
     return 0;
    }
  • NOIP_2001.PJ4:装箱问题 解题报告

    什么都不说了,简单的DP,直接上代码。
    唯一注意的一点,我不是用的剩余做DP值,而是和普通的DP一样,最后再用总质量剪掉这个DP值。

    C语言:

    #include <stdio.h>
    #define max(a, b) ((a)>(b)?(a):(b))
    
    int f[20001];
    
    int main(void)
    {
     int i, j, t;
     int v, m;
     scanf(%d%d, &v, &m);
     for(i = 0; i < m; i++){
     scanf(%d, &t);
     for(j = v; j >= t; j--){
     f[j] = max(f[j], f[j - t] + t);
     }
     }
     printf(%d\\n, v - f[v]);
     return 0;
    }
  • NOIP_2001.PJ3:求先序排列 解题报告

    在车上想了好久,终于找到了突破口,后序遍历不就是说最后一个节点就是当前书的根节点吗?那不就好说了,先把后序中的最后一个节点在中序中找到下标i,那小于i的便是左子树了,大于的的便是右子树了,而题目要的是先序,那就先把第i个(即后序的最后一个)输出来,然后再分别对左子树和右子树进行递归,然后后序从0~i-1都是左子树,后序i~len – i – 1(len是树的长度)是右子树。
    现在解释一下,0~i-1是后序的长度的原因是无论是先序还是后序还是中序都是同一棵树,长度必然相同,现在把一棵树分成了两棵树,长度一定还是相同的,所以这是可以成立的。
    然后别的什么我不说了,祝大家细心一点,这题我提交了n次才AC(哪里错了注释在里面的)。。

    C语言:

    #include <stdio.h>
    char mid[90];
    char last[90];
    char first[90];
    
    void srch(int start, int len, int start2)
    /* 第一个参数代表中序中的开头位置, len代表长度, start2 代表后序开头的位置 */
    {
     int i;
     if(len <= 0){ //掉了....=_=||| 
     return ;
     }
     printf(%c, last[start2 + len - 1]);
     for(i = 0; i < len; i++){
     if(mid[i + start] == last[start2 + len - 1]){
     break;
     }
     }
     srch(start, i, start2);
     srch(i + 1 + start, len - i - 1, i + start2);
     /* 巨大的错误, 第一个参数忘记加start了, 第三个参数忘记加start2了 */
    }
    
    int main(void)
    {
     scanf(%s %s, mid, last);
     srch(0, strlen(mid), 0);
     printf(\\n);
     return 0;
    }
  • NOIP_2001.PJ1:数的计数 解题报告

    刚看到题目被吓到了,下面有一个Hit(提示),说用高精度,高精度我以前写过一次,几乎忘干净了……后来琢磨了好久,发现能够使用动态规划,注意观察一下样例,输入是6,共有:
    6
    16
    26
    126
    36
    136
    设f[i]是以自然数i在最左边的个数,那么很容易发现方程f[i] = f[1] + f[2] + f[3] + …. + f[i/2] + 1;于是就产生了下面的代码:

    C语言:

    
    #include <stdio.h>
    unsigned f[1001];
    
    int main(void)
    {
     int n;
     int i, j;
     scanf(%d, &n);
     for(i = 1; i <= n; i++){
     f[i] = 1;
     for(j = 1; j <= i / 2; j++){
     f[i] += f[j];
     }
     }
     printf(%u\\n, f[n]);
     return 0;
    }

    有更好的算法的高手请留个言,我在不断学习中。。