分类: 技术

  • 校门外的树 解题报告

    这题没什么好说的,我觉得就是用数组判断一下,我用memset优化了一些慢效率的循环,本来以为会超时,但是结果完全相反,速度还挺快。。
    代码如下:

    C语言:

    #include <stdio.h>
    #include <string.h>
    char map[10001];
    
    int main(void)
    {
     int i, j;
     int a, b;
     int l, n;
     int ans = 0;
    
     scanf(%d%d, &l, &n);
     memset(map, 1, l + 1);
     for(i = 0; i < n; i++){
     scanf(%d%d, &a, &b);
     memset(map + a, 0, b - a + 1);
     }
     for(i = 0; i <= l; i++){
     if(map[i]){
     ans++;
     }
     }
     printf(%d\\n, ans);
    
     return 0;
    }
  • Noip 2006 作业调度方案

    题目的原描述如下,rqnoj和vijos的题目都不完全,少了一幅图片,表格也不清晰。。

    【问题描述】

    我们现在要利用m台机器加工n个工件,每个工件都有m道工序,每道工序都在不同的指定的机器上完成。每个工件的每道工序都有指定的加工时间。

    每个工件的每个工序称为一个操作,我们用记号j-k表示一个操作,其中j为1到n中的某个数字,为工件号;k为1到m中的某个数字,为工序号,例如2-4表示第2个工件第4道工序的这个操作。在本题中,我们还给定对于各操作的一个安排顺序。

    例如,当n=3,m=2时,“1-1,1-2,2-1,3-1,3-2,2-2”就是一个给定的安排顺序,即先安排第1个工件的第1个工序,再安排第1个工件的第2个工序,然后再安排第2个工件的第1个工序,等等。

    一方面,每个操作的安排都要满足以下的两个约束条件。

    (1) 对同一个工件,每道工序必须在它前面的工序完成后才能开始;

    (2) 同一时刻每一台机器至多只能加工一个工件。

    另一方面,在安排后面的操作时,不能改动前面已安排的操作的工作状态。

    由于同一工件都是按工序的顺序安排的,因此,只按原顺序给出工件号,仍可得到同样的安排顺序,于是,在输入数据中,我们将这个安排顺序简写为“1 1 2 3 3 2”。

    还要注意,“安排顺序”只要求按照给定的顺序安排每个操作。不一定是各机器上的实际操作顺序。在具体实施时,有可能排在后面的某个操作比前面的某个操作先完成。

    例如,取n=3,m=2,已知数据如下:

    工件号 机器号/加工时间
    工序1 工序2
    1 1/3 2/2
    2 1/2 2/5
    3 2/2 1/4

    则对于安排顺序“1 1 2 3 3 2”,下图中的两个实施方案都是正确的。但所需要的总时间分别是10与12。

    (原来这里有图,但搬运过来丢失了)

    当一个操作插入到某台机器的某个空档时(机器上最后的尚未安排操作的部分也可以看作一个空档),可以靠前插入,也可以靠后或居中插入。为了使问题简单一些,我们约定:在保证约束条件(1)(2)的条件下,尽量靠前插入。并且,我们还约定,如果有多个空档可以插入,就在保证约束条件(1)(2)的条件下,插入到最前面的一个空档。于是,在这些约定下,上例中的方案一是正确的,而方案二是不正确的。

    显然,在这些约定下,对于给定的安排顺序,符合该安排顺序的实施方案是唯一的,请你计算出该方案完成全部任务所需的总时间。

    【输入文件】

    输入文件jsp.in 的第1行为两个正整数,用一个空格隔开:m n(其中m(<20)表示机器数,n(<20)表示工件数)

    第2行: 个用空格隔开的数,为给定的安排顺序。

    接下来的2n行,每行都是用空格隔开的m个正整数,每个数不超过20。

    其中前n行依次表示每个工件的每个工序所使用的机器号,第1个数为第1个工序的机器号,第2个数为第2个工序机器号,等等。

    后n行依次表示每个工件的每个工序的加工时间。

    可以保证,以上各数据都是正确的,不必检验。

    【输出文件】

    输出文件jsp.out只有一个正整数,为最少的加工时间。

    【输入样例】

    2 3

    1 1 2 3 3 2

    1 2

    1 2

    2 1

    3 2

    2 5

    2 4

    【输出样例】

    10

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

    引用一位仁兄的话吧,现在在进行“素质教育”,光OI好已经是不行了,还要语文全面发展。。(哈哈,稍加修改了)
    整个程序的思路就是模拟,其实真的很容易,不过这种模拟题我还真没做过,所以Wa了N次,Wa的原因都写在注释里了,还有就是求ans的过程我也集成在主循环里了,降低了可读性,提高了点效率,Sorry各位观众了(算是读者啦)。
    代码如下:

    C语言:

    #include <stdio.h>
    #include <string.h>
    int train[361];
    int machine[19][19];
    int time[19][19];
    int used[19];
    int finished[19];
    char cpu[19][361];
    
    int main(void)
    {
     int i, j, k;
     int m, n;
     int t;
     int ans;
     scanf(%d%d, &m, &n);
     for(i = 0; i < m * n; i++){
     scanf(%d, &train[i]);
     train[i]--;
     }
     for(i = 0; i < n; i++){
     for(j = 0; j < m; j++){
     scanf(%d, &machine[i][j]);
     machine[i][j]--;
     }
     }
     for(i = 0; i < n; i++){
     for(j = 0; j < m; j++){
     scanf(%d, &time[i][j]);
     }
     }
    
     ans = 0;
     for(i = 0; i < m * n; i++){
     t = train[i];
     j = finished[t] - 1; // 因为当查找失败时,j的值需要向前增一, 
     do{ //所以在赋值的时候就减了一,然后用do-while 
     j++; //的形式,一进来就对j递增。 
     for(k = 0; k < time[t][used[t]]; k++){
     if(cpu[machine[t][used[t]]][j + k]){
     j = k + j; // 在最开始我就错在这行代码j = k + j 
     break; //的位置上,我把位置放在while(..)的上面, 
     } //也就导致了在有时候选择了处理器新的位置
     } //时在底下的memset功能工作不正常。因为我
     //是使用的finished而不是j,则在选择了位置
     //之后会导致一些预想不到的问题。
     }while(cpu[machine[t][used[t]]][j]);
     memset(cpu[machine[t][used[t]]] + j, t + 1, time[t][used[t]]);
     if(ans < j + time[t][used[t]]){
     ans = j + time[t][used[t]];
     }
     finished[t] = j + k;
     used[t]++;
     }
     // 最后还来一点注释,我把求ans集成
     //到了主循环里面,牺牲了一些可读性,对 
     //各位朋友说声Sorry..
     printf(%d\\n, ans);
     return 0;
    }
  • 金明的预算方案 解题报告

    这题刚拿到手,不知道怎么做,后来看到了题目里说每个主件最多有0,1,2个附件,那也就是说对于每个主件及其附件而言,最多有如下几种情况:不买一件;只买主件;买主件及附件1;买主件及附件2;买主件及附件1,2都买。
    这么就好DP了,代码如下:

    C语言:

    #include <stdio.h>
    #define max(a, b) ((a)>(b)?(a):(b))
    int f[32001];
    int v[61], p[61];
    int lenth[61];
    int have[61][2];
    char not[61];
    
    int main(void)
    {
     int i, j, k;
     int n, m;
     int a, b, c;
     scanf(%d%d, &n, &m);
     for(i = 1; i <= m; i++){
     scanf(%d%d%d, &a, &b, &c);
     v[i] = a;
     p[i] = a * b;
     if(c != 0){
     have[c][lenth[c]++] = i;
     not[i] = 1;
     }
     }
     for(i = 1; i <= m; i++){
     if(not[i]){
     continue;
     }
     for(j = n; j >= v[i]; j -= 10){
    /* if(f[j] > f[j - v[i]] + p[i]){
     //这里必须不能是小于等于
     continue;
     }*/
     f[j] = max(f[j], f[j - v[i]] + p[i]);
     for(k = 0; k < lenth[i]; k++){
     if((j - v[have[i][k]] - v[i]) >= 0){
     //必须要是大于等于0 
     f[j] = max(f[j],
     f[j - v[have[i][k]] - v[i]] + p[i]
     + p[have[i][k]]);
     }
     }
     if((lenth[i] == 2) && ((j - v[i] - v[have[i][0]] - v[have[i][1]]) >= 0)){
     //必须要是大于等于0
     f[j] = max(f[j], f[j - v[i] - v[have[i][0]] - v[have[i][1]]] + p[i] + p[have[i][0]] + p[have[i][1]]);
     }
     }
     }
     printf(%d\\n, f[n]);
     return 0;
    }
  • 陶陶摘苹果 解题报告

    这种题目我确实不想写解题报告,没什么好写的,说它是贪心都不算,就是简单的模拟下。代码如下:

    C语言:

    #include <stdio.h>
    int apples[10];
    
    int main(void)
    {
     int ans;
     int i, h;
     for(i = 0; i < 10; i++){
     scanf(%d, &apples[i]);
     }
     scanf(%d, &h);
     h += 30;
     for(i = ans = 0; i < 10; i++){
     if(apples[i] <= h){
     ans++;
     }
     }
     printf(%d\\n, ans);
     return 0;
    }
  • 采药 解题报告

    就是一个经典的01背包,能够把时间看做体积,价值嘛就是价值,代码如下:

    C语言:

    #include <stdio.h>
    #define max(a, b) ((a)>(b)?(a):(b))
    int f[1001];
    
    int main(void)
    {
     int i, j;
     int m, n;
     int a, b;
     scanf(%d%d, &m, &n);
     for(i = 0; i < n; i++){
     scanf(%d%d, &a, &b);
     for(j = m; j >= a; j--){
     f[j] = max(f[j], f[j - a] + b);
     }
     }
     printf(%d\\n, f[m]);
     return 0;
    }
  • 能量项链 解题报告

    额,花了我三四个小时的时间,终于知道了,是动态规划的题目(我以前做过,不过那个时候真是不知不觉过去的。),我以前只做过背包的题目,这个题目是这暑假以来觉得最复杂的题目了,因为非动态规划的代码一看就能看懂,动态规划的,方程没想通你就看不懂代码!
    我的方程是: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;
    }