蓝桥杯练习系统习题-算法训练2

版权声明:本文为本人辛苦原创文章,如果需要转载,请注明出处,谢谢!!! https://blog.csdn.net/sihai12345/article/details/79238561

文章推荐

蓝桥杯练习系统习题-算法训练2

题目搜索方式:Ctrl+F—-> 输入题目名称—>定位到解答.

入门训练(详见 算法-蓝桥杯习题(1-1))

基础练习(详见 算法-蓝桥杯习题(2-1))

基础练习(详见 算法-蓝桥杯习题(2-2))

算法训练(详见 算法-蓝桥杯习题(3-1))

算法训练(详见 算法-蓝桥杯习题(3-2))

算法训练(详见 算法-蓝桥杯习题(3-3))

算法训练(详见 算法-蓝桥杯习题(3-4))

算法训练(详见 算法-蓝桥杯习题(3-5))

算法训练(详见 算法-蓝桥杯习题(3-6))

算法提高(详见 算法-蓝桥杯习题(4-1))

算法提高(详见 算法-蓝桥杯习题(4-2))

历届试题(详见 算法-蓝桥杯习题(5-1))

历届试题(详见 算法-蓝桥杯习题(5-2))


算法训练 装箱问题

问题描述
  有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。
  要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入格式
  第一行为一个整数,表示箱子容量;
  第二行为一个整数,表示有n个物品;
  接下来n行,每行一个整数表示这n个物品的各自体积。
输出格式
  一个整数,表示箱子剩余空间。
  样例输入
  24
  6
  8
  3
  12
  7
  9
  7
样例输出
0

 #include <stdio.h>  
 #include <string.h>  
 #include <malloc.h>  
 #define V 20001  
 #define N 30  
 main()  
 {  
     int f[V];  
     int i,j;  
     int n,v;  
     int T[V];  
     memset(f,0,sizeof(f));  
     memset(T,0,sizeof(T));  
     scanf("%d%d",&n,&v);  
     for(i=1;i<=v;i++)  
         scanf("%d",&f[i]);  
     for(i=1;i<=v;i++)  
     {      
         for(j=n;j>=f[i];j--)  
         {  
             if(f[i]<=j&&f[i]+T[j-f[i]]>T[j])  
                 T[j] = f[i]+T[j-f[i]];    
         }  
      }  
      printf("%d\n",n-T[n]);  
     return 0;  
 }  

算法训练 数的划分

问题描述
  将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
  例如:n=7,k=3,下面三种分法被认为是相同的。
  1,1,5; 1,5,1; 5,1,1;
  问有多少种不同的分法。
输入格式
  n,k
输出格式
  一个整数,即不同的分法
样例输入
7 3
样例输出
4 {四种分法为:1,1,5;1,2,4;1,3,3;2,2,3;}
数据规模和约定
  6<n<=200,2<=k<=6

 #include <stdio.h>  
 int main()  
 {  
     int i,j,n,k,a[201][7]={0};  
     a[1][1] = 1;  
     scanf("%d%d",&n,&k);  
     for (i = 2; i <= n; i++)  
         for (j = 1; j <= k; j++)  
             if (i >= j)  
                 a[i][j] = a[i - j][j] + a[i - 1][j - 1];  
     printf("%d",a[n][k]);  
     return 0;  
 }  

算法训练 一元三次方程求解

问题描述
  有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。要求三个实根。。
输入格式
  四个实数:a,b,c,d
输出格式
  由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位
样例输入
1 -5 -4 20
样例输出
-2.00 2.00 5.00
数据规模和约定
  |a|,|b|,|c|,|d|<=10

 #include<stdio.h>  
 float a,b,c,d,x=-100,t,y;  
 float f(float z)  
 {  
     return a*z*z*z+b*z*z+c*z+d;  
 }  
 int main()  
 {  
     scanf("%f%f%f%f",&a,&b,&c,&d);  
     t=f(x);  
     while(x<=100)  
     {  
         y=f(x);  
         if(y*t<=0)printf("%.2f ",x);  
         x=x+0.001;t=y;  
     }  
     printf("\n");  
     return 0;  
 }  

算法训练 统计单词个数

问题描述
  给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份 (1<k<=40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例 如字符串this中可包含this和is,选用this之后就不能包含th)。
  单词在给出的一个不超过6个单词的字典中。
  要求输出最大的个数。
输入格式
  第一行有二个正整数(p,k)
  p表示字串的行数;
  k表示分为k个部分。
  接下来的p行,每行均有20个字符。
  再接下来有一个正整数s,表示字典中单词个数。(1<=s<=6)
  接下来的s行,每行均有一个单词。
输出格式
  每行一个整数,分别对应每组测试数据的相应结果。
样例输入
1 3
thisisabookyouareaoh
4
is
a
ok
sab
样例输出
7
数据规模和约定
  长度不超过200,1<k<=40,字典中的单词数不超过6。

 #include <stdio.h>  
 #include <string.h>  
 #define InfiniteMin -999999999  
 int p,k,s;  
 char str[10][21];  
 char word[6][16];  
 int flag[6];  
 int Cnt[201][201];  
 int getCnt(int a,int b)  
 {  
     int i,j,m,count=0;  
     if(Cnt[a][b]<=0)  
     {  
         for (i=a;i<=b;i++)  
             for (j=0;j<s;j++)  
                 if(word[j][0]==str[i/20][i%20])  
                 {  
                     for(m=1;word[j][m]!='\0'&&i+m<=b;m++)  
                         if(word[j][m]!=str[(i+m)/20][(i+m)%20])  
                             break;  
                     if(word[j][m]=='\0')  
                     {  
                         count++;  
                         break;   
                     }  
                 }  
         Cnt[a][b]=count;  
     }  
     return Cnt[a][b];  
 }  
 int main()  
 {  
     int i,u,K;  
     int max,temp;  
     int F[201][41];  
     scanf("%d%d",&p,&k);  
     for(i=0;i<p;i++)  
          scanf("%s",str[i]);  
     scanf("%d",&s);  
     for(i=0;i<s;i++)  
         scanf("%s",word[i]);  
     for(i=0;i<20*p;i++)  
         F[i][1]=getCnt(0,i);  
     for(K=2;K<=k;K++)  
         for(i=k;i<p*20;i++)  
         {  
             max=InfiniteMin;  
             for(u=K-1;u<=i-1;u++)  
             {  
                 temp=F[u][K-1]+getCnt(u+1,i);  
                 max=max>temp?max:temp;  
             }  
             F[i][K]=max;  
         }  
     printf("%d",F[p*20-1][k]);  
     return 0;  
 }  

算法训练 Car的旅行路线

问题描述
  又到暑假了,住在城市A的Car想和朋友一起去城市B旅游。她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一 条笔直的高速铁路,第I个城市中高速铁路了的单位里程价格为Ti,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为t。
  那么Car应如何安排到城市B的路线才能尽可能的节省花费呢?她发现这并不是一个简单的问题,于是她来向你请教。
  找出一条从城市A到B的旅游路线,出发和到达城市中的机场可以任意选取,要求总的花费最少。
输入格式
  的第一行有四个正整数s,t,A,B。
  S表示城市的个数,t表示飞机单位里程的价格,A,B分别为城市A,B的序号,(1<=A,B<=S)。
  接下来有S行,其中第I行均有7个正整数xi1,yi1,xi2,yi2,xi3,yi3,Ti,这当中的(xi1,yi1),(xi2,yi2),(xi3,yi3)分别是第I个城市中任意三个机场的坐标,T I为第I个城市高速铁路单位里程的价格。
输出格式
  共有n行,每行一个数据对应测试数据,保留一位小数。
样例输入
1
1 10 1 3
1 1 1 3 3 1 30
2 5 7 4 5 2 1
8 6 8 8 11 6 3
样例输出
47.55
数据规模和约定
  0<S<=100,

 //C++  
 #include <float.h>   
 #include <math.h>   
 #include <stdio.h>   
 #include<cstring>  
 #define Sqr(x) ((x)*(x))   
 double s[400][400], x[400], y[400];   
 int main()   
 {   
     int a, b, n, tt,t,i,j,k,N;   
         double res = DBL_MAX;   
         scanf("%d%d%d%d", &n, &tt, &a, &b);   
         a-=1;   
         b-=1;   
         for ( i = 0; i < n; ++i)   
         {   
             for ( j = 0; j < 3; ++j)   
                 scanf("%lf%lf", &x[i*4+j], &y[i*4+j]);   
             scanf("%d", &t);   
             for (j = 0; j < 3; ++j)   
             {   
                 double x1 = x[i*4+(j+1)%3], y1 = y[i*4+(j+1)%3],   
                     x2 = x[i*4+(j+2)%3], y2 = y[i*4+(j+2)%3];   
                 if (fabs((x[i*4+j]-x1)*(x[i*4+j]-x2)+(y[i*4+j]-y1)*(y[i*4+j]-y2)) < 1E-7)   
                     x[i*4+3] = x1+x2-x[i*4+j], y[i*4+3] = y1+y2-y[i*4+j];   
             }   
             for ( j = 0; j < 4; ++j)   
                 for (k = 0; k < 4; ++k)   
                     s[i*4+j][i*4+k] = t*sqrt(Sqr(x[i*4+j]-x[i*4+k])+Sqr(y[i*4+j]-y[i*4+k]));   
             for ( j = 0; j < i*4; ++j)   
                 for ( k = 0; k < 4; ++k)   
                     s[i*4+k][j] = tt*sqrt(Sqr(x[i*4+k]-x[j])+Sqr(y[i*4+k]-y[j])),   
                     s[j][i*4+k] = s[i*4+k][j];   
         }   
         for ( k = 0; k < n*4; ++k)   
             for (i = 0; i < n*4; ++i)   
                 for ( j = 0; j < n*4; ++j)   
                     if (s[i][k]+s[k][j] < s[i][j])   
                         s[i][j] = s[i][k]+s[k][j];   
         for (i = 0; i < 4; ++i)   
             for ( j = 0; j < 4; ++j)   
                 if (s[a*4+i][b*4+j] < res)   
                     res = s[a*4+i][b*4+j];   
         printf("%.1lf\n", res);   
     return 0;  
 }  

算法训练 麦森数

问题描述
  形如2P-1的素数称为麦森数,这时P一定也是个素数。但反过来不一定,即如果P是个素数,2P-1不一定也是素数。到1998年底,人们已找到了37个麦森数。最大的一个是P=3021377,它有909526位。麦森数有许多重要应用,它与完全数密切相关。
  任务:从文件中输入P(1000<P<3100000),计算2P-1的位数和最后500位数字(用十进制高精度数表示)
输入格式
  文件中只包含一个整数P(1000<P<3100000)
输出格式
  第一行:十进制高精度数2P-1的位数。
  第2-11行:十进制高精度数2P-1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0)
  不必验证2P-1与P是否为素数。
样例输入
1279
样例输出
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087

 #include <stdio.h>  
 #include <string.h>  
 #include <math.h>  
 #define LEN 125  //每数组元素存放十进制的4位,因此数组最多只要125个元素即可  
 //Multiply函数功能是计算高精度乘法a*b,结果的末500位放在a中  
 void Multiply(int *a,int *b)  
 {  
     int i,j;  
     int nCarry; //存放进位  
     int nTmp;  
     int c[LEN];  //存放结果的末500位  
     memset(c,0,sizeof(int)*LEN);  
     for(i=0;i<LEN;i++)  
     {  
         nCarry=0;  
         for(j=0;j<LEN-i;j++)  
         {  
             nTmp=c[i+j]+a[j]*b[i]+nCarry;  
             c[i+j]=nTmp%10000;  
             nCarry=nTmp/10000;  
         }  
     }  
     memcpy(a,c,LEN*sizeof(int));  
 }  
 int main()  
 {  
     int i,p;  
     int anPow[LEN];  //存放不断增长的2的次幂  
     int aResult[LEN];  //存放最终结果的末500位  
     scanf("%d",&p);  
     printf("%d\n",(int)(p*log10(2))+1);  
     //下面将2的次幂初始化为2^(2^0)(a^b表示a的b次方),最终结果初始化为1  
     anPow[0]=2;  
     aResult[0]=1;  
     for(i=1;i<LEN;i++)  
     {  
         anPow[i]=0;  
         aResult[i]=0;  
     }  
     //下面计算2的p次方  
     while(p>0)  //p=0则说明p中的有效位都用过了,不需要再计算下去  
     {  
         if(p&1)  //判断此时p中最低位是否为1  
             Multiply(aResult,anPow);  
         p>>=1;  
         Multiply(anPow,anPow);  
     }  
     aResult[0]--;  //2的p次方算出后减1  
     //输出结果  
     for(i=LEN-1;i>=0;i--)  
     {  
         if(i%25==12)  
             printf("%02d\n%02d",aResult[i]/100,aResult[i]%100);  
         else  
         {  
             printf("%04d",aResult[i]);  
             if(i%25==0)  
                 printf("\n");  
         }  
     }  
     return 0;  
 }  

算法训练 FBI树

问题描述
  我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。
  FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
  1)T的根结点为R,其类型与串S的类型相同;
  2)若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。
  现在给定一个长度为2N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。
输入格式
  第一行是一个整数N(0 <= N <= 10),第二行是一个长度为2N的“01”串。
输出格式
  包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
样例输入
3
10001011
样例输出
IBFBBBFIBFIIIFF
数据规模和约定
  对于40%的数据,N <= 2;
  对于全部的数据,N <= 10。
  注:
  [1] 二叉树:二叉树是结点的有限集合,这个集合或为空集,或由一个根结点和两棵不相交的二叉树组成。这两棵不相交的二叉树分别称为这个根结点的左子树和右子树。
  [2] 后序遍历:后序遍历是深度优先遍历二叉树的一种方法,它的递归定义是:先后序遍历左子树,再后序遍历右子树,最后访问根。

 #include <stdio.h>  
 #include <malloc.h>  
 char in[1025];  
 typedef struct node  
 {  
     char data;  
     struct node *lchild,*rchild;  
 }Binode;  
 Binode *create(int a,int b)  
 {  
     char ch;  
     Binode *p;  
     int i,count0=0,count1=0,flag=0;  
 //  scanf("%c",&ch);  
 //  getchar();  
     if(a==b)  
     {  
         (in[a]=='0')?(ch='B'):(ch='I');  
         p=(Binode *)malloc(sizeof(Binode));  
         p->data=ch;  
         p->lchild=NULL;  
         p->rchild=NULL;  
         return p;  
     }  
     for(i=a;i<=b;i++)  
     {  
         (in[i]=='0')?(count0++):(count1++);  
         if(count0 && count1)  
         {  
             ch='F';  
             flag=1;  
             break;  
         }  
     }  
     if(!flag)  
     {  
         if(!count0)  
             ch='I';  
         else  
             ch='B';  
     }  
     p=(Binode *)malloc(sizeof(Binode));  
     p->data=ch;  
     p->lchild=create(a,((b+1)-a)/2+a-1);  
     p->rchild=create(((b+1)-a)/2+a,b);  
     return p;  
 }  
 void display(Binode *p)  
 {  
     if(!p)  
         return;  
     display(p->lchild);  
     display(p->rchild);  
     printf("%c",p->data);  
 }  
 int main()  
 {  
     Binode *r;  
     char c;  
     int p=0,n;  
     scanf("%d",&n);  
     getchar();  
     while(1)  
     {  
         c=getchar();  
         if(c=='\n')  
             break;  
         in[p++]=c;  
     }  
     r=create(0,p-1);  
     display(r);  
     return 0;  
 }  

算法训练 星际交流

问题描述
  人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样 的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回 答。
  火星人用一种非常简单的方式来表示数字——掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为1,2,3……。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。
  一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指——拇指、食指、中指、无名指和小指分别编号为1,2,3,4和5,当它们按正常顺序排列 时,形成了5位数12345,当你交换无名指和小指的位置时,会形成5位数12354,当你把五个手指的顺序完全颠倒时,会形成54321,在所有能够形 成的120个5位数中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指时能够形成的6个 3位数和它们代表的数字:
  三进制数
  123
  132
  213
  231
  312
  321
  代表的数字
  1
  2
  3
  4
  5
  6
  现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科 学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。
输入格式
  包括三行,第一行有一个正整数N,表示火星人手指的数目(1 <= N <= 10000)。第二行是一个正整数M,表示要加上去的小整数(1 <= M <= 100)。下一行是1到N这N个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。
输出格式
  只有一行,这一行含有N个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。
样例输入
5
3
1 2 3 4 5
样例输出
1 2 4 5 3
数据规模和约定
  对于30%的数据,N<=15;
  对于60%的数据,N<=50;
  对于全部的数据,N<=10000;

 #include<stdio.h>  
 int main()  
 {  
     int n,w,i1,i2;  
     int a[100000];  
     scanf("%d%d",&n,&w);  
     for(i1=0;i1<n;i1++)  
          scanf("%d",&a[i1]);  
     while(w--)  
     {   int max=a[n-1],min;  
         for(i1=n-2;i1>=0;i1--)  
         {  
             if(max<a[i1])  
                  {max=a[i1];  
                  continue;  
                  }  
             min=max;   
             int xiabiao=-1;      
             for(i2=i1+1;i2<=n-1;i2++)  
             {  
                 if(a[i2]>a[i1])  
                   {  
                       if(min>=a[i2])  
                       {  
                         min=a[i2];  
                         xiabiao=i2;  
                       }  
                 }  
             }     
             int k=a[i1];  
             a[i1]=min;  
             a[xiabiao]=k;  
             for(i2=1;i1+i2<n-i2;i2++)  
             {  
                 int kp=a[i2+i1];  
                 a[i2+i1]=a[n-i2];  
                 a[n-i2]=kp;  
             }             
            break;  
         }  
     }  
 for(i1=0;i1<n;i1++)  
        printf("%d ",a[i1]);     
        printf("\n");   
     return 0;  
 }  

算法训练 校门外的树

问题描述
  某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数 轴上的每个整数点,即0,1,2,……,L,都种有一棵树。
  由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已 知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树 都移走后,马路上还有多少棵树。
输入格式
  输入文件的第一行有两个整数L(1 <= L <= 10000)和 M(1 <= M <= 100),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点 和终止点的坐标。
输出格式
  输出文件包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。
样例输入
500 3
150 300
100 200
470 471
样例输出
298
数据规模和约定
  对于20%的数据,区域之间没有重合的部分;
  对于其它的数据,区域之间有重合的情况。

 #include<stdio.h>  
 typedef struct  
 {  
 int start;  
 int end;  
 int flag;  
 }extent;  
 int main()  
 {  
 int L,M,i,j;  
 extent e[101];  
 scanf("%d%d",&L,&M);  
 for(i=1;i<=M;i++)  
 {  
 scanf("%d%d",&(e[i].start),&(e[i].end));  
 e[i].flag=1;  
 for(j=1;j<i;j++)  
 {  
 if(!(e[i].end<e[j].start||e[i].start>e[j].end)&&e[j].flag)  
 {  
 e[j].flag=0;  
 if(e[i].start>e[j].start)  
 e[i].start=e[j].start;  
 if(e[i].end<e[j].end)  
 e[i].end=e[j].end;  
 }  
 }//调整区间   
 }  
 for(i=1;i<=M;i++)  
 if(e[i].flag)  
 L=L-(e[i].end-e[i].start+1);  
 printf("%d",L+1);  
 return 0;  
 }  

算法训练 入学考试

问题描述
  辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
  如果你是辰辰,你能完成这个任务吗?
输入格式
  第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
  包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例输入
70 3
71 100
69 1
1 2
样例输出
3
数据规模和约定
  对于30%的数据,M <= 10;
  对于全部的数据,M <= 100。


 #include <stdio.h>  
 int totalTime, medics;  
 int value[100];  
 int time[100];  
 int max[1001];  
 int main()   
 {  
     scanf("%d%d", &totalTime, &medics);  
     int i, t;  
     for (i=0; i<medics; i++)  
     {  
         scanf("%d%d", &time[i], &value[i]);  
     }  
     for (i=0; i<medics; i++)  
     {  
         for (t=totalTime; t>0; t--)  
         {  
             if (time[i] <= t)  
             {  
                 if (value[i] + max[t-time[i]] > max[t])//第i个的价值+不选第i个且用时为t-time[i-1]时最大价值  
                 {  
                     max[t] = value[i] + max[t-time[i]];  
                 }  
             }  
         }  
     }  
     printf("%d\n", max[totalTime]);  
     return 0;  
 }  

算法训练 开心的金明

问题描述
  金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎 么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一 个重要度,分为5等:用整数1~{5}表示,第{5}等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过{N}元(可以等于{N}元)的前提下,使每件物品的价格与重要度的乘积的总和最大。<{b}\frac{{r}}{>}  设第{j}件物品的价格为{v}{\left[{j}\right]},重要度为{w}{\left[{j}\right]},共选中了{k}件物品,编号依次为{j}{1},{j}{2},……,{j}{k},则所求的总和为:<{b}\frac{{r}}{>}  {v}{\left[{j}{1}\right]}<{e}{m}>{w}{\left[{j}{1}\right]}+{v}{\left[{j}{2}\right]}\frac{<}{{e}}{m}>{w}{\left[{j}{2}\right]}+…+{v}{\left[{j}{k}\right]}<{e}{m}>{w}{\left[{j}{k}\right]}。(其中\frac{<}{{e}}{m}>为乘号)<{b}\frac{{r}}{>}  请你帮助金明设计一个满足要求的购物单。<{b}\frac{{r}}{>}输入格式<{b}\frac{{r}}{>}  输入文件的第{1}行,为两个正整数,用一个空格隔开:<{b}\frac{{r}}{>}  {N}{m}<{b}\frac{{r}}{>}  (其中{N}(<{30000})表示总钱数,{m}(<{25})为希望购买物品的个数。)<{b}\frac{{r}}{>}  从第{2}行到第{m}+{1}行,第{j}行给出了编号为{j}-{1}的物品的基本数据,每行有{2}个非负整数<{b}\frac{{r}}{>}  {v}{p}<{b}\frac{{r}}{>}  (其中{v}表示该物品的价格{\left({v}\le{10000}\right)},{p}表示该物品的重要度{\left({1}\right.}~5))
输出格式
  输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。
样例输入
1000 5
800 2
400 5
300 5
400 3
200 2
样例输出
3900
数据规模和约定

 #include <stdio.h>  
 long mnum[30000];   
 long objprice[25]; //第i件商品的价格   
 long objk[25];     //第i件商品的价格与权值的积   
 int M,N;    //M表示金钱总数,N表示商品总数   
 int main()  
 {  
     int i,m;  
     int maxp = 0;  
     scanf("%u%u", &M, &N);   //M表示金钱总数,N表示商品总数   
     for(i=0;i<N;++i) //依次输入第i件商品的价格与权值,并计算出商品的价格与权值之积   
     {  
         scanf("%u%u", &objprice[i], &m);  
         objk[i] = m*objprice[i];  
     }  
     for(i=0;i<N;++i)    
     {  
         for(m=0;m<M-objprice[i];++m)  
             if (mnum[m+objprice[i]] + objk[i] > mnum[m])  
                 mnum[m] = mnum[m+objprice[i]] + objk[i];  
     }     
     for(i=0;i<M;++i)   
         if (mnum[i] > maxp)  
             maxp = mnum[i];  
     printf("%u", maxp);  
     return 0;  
 }  

算法训练 JAM计数法

问题描述
  Jam是个喜欢标新立异的科学怪人。他不使用阿拉伯数字计数,而是使用小写英文字母计数,他觉得这样做,会使世界更加丰富多彩。在他的计数法中,每个数字的位数都是相同的(使用相同个数的字母),英文字母按原先的顺序,排在前面的字母小于排在它后面的字母。我们把这样的“数字”称为Jam数字。在Jam数字中,每个字母互不相同,而且从左到右是严格递增的。每次,Jam还指定使用字母的范围,例如,从2到10,表示只能使用{b,c,d,e,f,g,h,i,j}这些字母。如果再规定位数为5,那么,紧接在Jam数字“bdfij”之后的数字应该是“bdghi”。(如果我们用U、V依次表示Jam数字“bdfij”与“bdghi”,则U,且不存在Jam数字P,使U)。你的任务是:对于从文件读入的一个Jam数字,按顺序输出紧接在后面的5个Jam数字,如果后面没有那么多Jam数字,那么有几个就输出几个。
输入格式
  有2行,第1行为3个正整数,用一个空格隔开:
  s t w
  (其中s为所使用的最小的字母的序号,t为所使用的最大的字母的序号。w为数字的位数,这3个数满足:1≤s<T≤26, 2≤w≤t-s )
  第2行为具有w个小写字母的字符串,为一个符合要求的Jam数字。
  所给的数据都是正确的,不必验证。
输出格式
  最多为5行,为紧接在输入的Jam数字后面的5个Jam数字,如果后面没有那么多Jam数字,那么有几个就输出几个。每行只输出一个Jam数字,是由w个小写字母组成的字符串,不要有多余的空格。
样例输入
2 10 5
bdfij
样例输出
bdghi
bdghj
bdgij
bdhij
befgh

 #include <stdio.h>  
 int main()  
 {  
     int s,t,w;  
     char in[26];  
     int ad[26],i,j,k,flag,st;  
     scanf("%d%d%d",&s,&t,&w);  
     getchar();  
     for(i=0;i<w;i++)  
     {  
         scanf("%c",&in[i]);  
         ad[i]=in[i]-'a'+1;  
     }  
 //  for(i=0;i<w;i++)  
 //      printf("%d ",ad[i]);//+'a'-1);  
 //  printf("\n");  
     for(i=0;i<5;i++)  
     {  
         if(ad[0]==(t-w+1))  
             break;  
     //  flag=0;  
         for(j=w-1;j>=0;j--)  
         {  
             if(ad[j]==t-w+j+1)  
                 continue;  
     //      if(!flag)  
     //      {  
             //  ad[j]++;  
                 st=++ad[j];  
             //  k=j;  
                 for(k=j+1;k<w;k++)  
                     ad[k]=++st;  
                 for(k=0;k<w;k++)  
                     printf("%c",ad[k]+'a'-1);  
                 printf("\n");  
                 flag=1;  
                 break;  
     //      }  
         }  
     }  
     return 0;  
 }  

算法训练 数列

问题描述
  给定一个正整数k(3≤k≤15),把所有k的方幂及所有有限个互不相等的k的方幂之和构成一个递增的序列,例如,当k=3时,这个序列是:
  1,3,4,9,10,12,13,…
  (该序列实际上就是:30,31,30+31,32,30+32,31+32,30+31+32,…)
  请你求出这个序列的第N项的值(用10进制数表示)。
  例如,对于k=3,N=100,正确答案应该是981。
输入格式
  只有1行,为2个正整数,用一个空格隔开:
  k N
  (k、N的含义与上述的问题描述一致,且3≤k≤15,10≤N≤1000)。
输出格式
  计算结果,是一个正整数(在所有的测试数据中,结果均不超过2.1*109)。(整数前不要有空格和其他符号)。
样例输入
3 100
样例输出
981

 #include <stdio.h>  
 int main()  
 {  
     int re[1000],k,n;  
     int p=0,t=1,i,j,f;  
     re[0]=1;j=0,f=1;  
     scanf("%d%d",&k,&n);  
     for(i=1;i<n;i++)  
     {  
         if(f==re[j])  
         {  
             f*=k;  
             re[i]=f;  
 //          printf("%d ",f);  
             j=0;  
             continue;  
         }  
         t=f+re[j++];  
         re[i]=t;  
 //      printf("%d ",t);  
     }  
     printf("%d",re[i-1]);  
     return 0;  
 }  

算法训练 纪念品分组

问题描述
  元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念品价值 相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品,并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时 间内发完所有纪念品,乐乐希望分组的数目最少。
  你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入格式
  输入包含n+2行:
  第1行包括一个整数w,为每组纪念品价格之和的上限。
  第2行为一个整数n,表示购来的纪念品的总件数。
  第3~n+2行每行包含一个正整数pi (5 <= pi <= w),表示所对应纪念品的价格。
输出格式
  输出仅一行,包含一个整数,即最少的分组数目。
样例输入
100
9
90
20
20
30
50
60
70
80
90
样例输出
6
数据规模和约定
  50%的数据满足:1 <= n <= 15
  100%的数据满足:1 <= n <= 30000, 80 <= w <= 200

 #include <stdio.h>  
 void qsort(int i,int j);  
     int a[30000];  
     void qsort(int i,int j){       
     int x,p,q;      
     x=a[i]; p=i; q=j;      
     while (i<j)       
     {             
         while ((i<j)&&(a[j]>x))  
         j--;           
         if (i<j)            
         {               
             a[i]=a[j];               
             i++;            
         }     
         while ((i<j)&&(a[i]<x))  
         i++;  
         if (i<j)             
         {                
             a[j]=a[i];                
             j--;            
         }       
     }       
     a[i]=x;       
     if (p<i-1)   
     qsort(p,i-1);       
     if (i+1<q)   
     qsort(i+1,q);  
 }  
 main(){        
     int n,w,s,i,j;     
     scanf("%d%d",&w,&n);  
     for (i=0;i<n;i++)   
     scanf("%d",&a[i]);  
     qsort(0,n-1);        
     i=0;  
     j=n-1;   
     s=0;    
     while (i<j)       
     {             
         s++;             
         if (a[i]+a[j]<=w)             
         {                
         i++;                
         j--;             
         }              
         else   
         j--;       
     }   
     if ((i==j)&&(a[i]<=w))   
     s++;         
     printf("%d",s);       
     getchar();        
     getchar();  
     return(0);  
 }  

算法训练 传球游戏

 【问题描述】
  上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。
  游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没传出去的那个同学就是败者,要给大家表演一个节目。
  聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了m次以后,又回到小蛮手里。两种传球的方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有3个同学1号、2号、3号,并假设小蛮为1号,球传了3次回到小蛮手里的方式有1->2->3->1和1->3->2->1,共2种。
输入格式
  共一行,有两个用空格隔开的整数n,m(3<=n<=30,1<=m<=30)。
输出格式
  t共一行,有一个整数,表示符合题意的方法数。
样例输入
3 3
样例输出
2
数据规模和约定
  40%的数据满足:3<=n<=30,1<=m<=20
  100%的数据满足:3<=n<=30,1<=m<=30

 #include <stdio.h>   
 int n,m;  
 int at1(int x)  
 {  
     if(x<1) return x + n;     
     if(x>n) return x - n;    
     return x;  
 }int main()  
 {  
     int f[31][31] = {0},i,j;    
     scanf("%d%d",&n,&m);     
     f[1][2] = f[1][n] = 1;   
     for(i = 2;i <= m;i++)      
     for(j = 1;j <= n;j++)  
         f[i][j] = f[i - 1][at1(j - 1)] + f[i - 1][at1(j + 1)];   
     printf("%d",f[m][1]);   
     return 0;  
 }  

算法训练 传纸条

问题描述
  小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
  在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
  还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。
输入格式
  输入第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1<=m,n<=50)。
  接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。
输出格式
  输出一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
样例输入
3 3
0 3 9
2 8 5
5 7 0
样例输出
34
数据规模和约定
  30%的数据满足:1<=m,n<=10
  100%的数据满足:1<=m,n<=50

 #include<stdio.h>      
 int n,m;     
 int i,j,k;     
 int Map[51][51];     
 int F[111][51][51];     
 int Max(int a,int b,int c,int d)     
 {     
     if(a>=b&&a>=c&&a>=d)     
         return a;     
     if(b>=a&&b>=c&&b>=d)     
         return b;     
     if(c>=a&&c>=b&&c>=d)     
         return c;     
     if(d>=a&&d>=b&&d>=c)     
         return d;     
 }     
 int main()     
 {     
     scanf("%d%d",&n,&m);     
     for(i=1;i<=n;i++)     
         for(j=1;j<=m;j++)     
             scanf("%d",&Map[i][j]);     
     for(k=1;k<=n+m-2;k++)     
         for(i=1;i<=n;i++)     
             for(j=1;j<=n;j++)     
                 if(i==n&&j==n&&k==n+m-2)     
                     F[k][i][j]=Max(F[k-1][i-1][j],F[k-1][i][j-1],F[k-1][i][j],F[k-1][i-1][j-1])+Map[i][k+2-i]+Map[j][k+2-j];     
                 else  if(i!=j&&k+2-i>=1&&k+2-j>=1)     
                     F[k][i][j]=Max(F[k-1][i-1][j],F[k-1][i][j-1],F[k-1][i][j],F[k-1][i-1][j-1])+Map[i][k+2-i]+Map[j][k+2-j];     
     printf("%d",F[n+m-2][n][n]);     
     return 0;     
 }    

算法训练 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 整除。
输出格式
  输出共n 行。每组输入数据的输出结果占一行,为一个整数。
  对于每组数据:若不存在这样的 x,请输出0; 若存在这样的 x,请输出满足条件的x 的个数;
样例输入
2
41 1 96 288
95 1 37 1776
样例输出
6
2
样例说明
  第一组输入数据,x 可以是9、18、36、72、144、288,共有6 个。
  第二组输入数据,x 可以是48、1776,共有2 个。
数据规模和约定
  对于 50%的数据,保证有1≤a0,a1,b0,b1≤10000 且n≤100。
  对于 100%的数据,保证有1≤a0,a1,b0,b1≤2,000,000,000 且n≤2000。

 #include <stdio.h>  
 int gcd(int x,int y)  
 {  
     return y?gcd(y,x%y):x;  
 }  
 int main()  
 {  
     int n,a0,a1,b0,b1,x,cnt;  
     scanf("%d",&n);  
     while(n--)  
     {  
         scanf("%d%d%d%d",&a0,&a1,&b0,&b1);  
         cnt=0;  
         for(x=1;x*x<=b1;x++)  
         {  
             if(b1%x==0)//如果x确实是b1的约数   
             {  
                 if(x%a1==0)//如果x确实是a1的倍数  
                 {   
                     if(gcd(x,a0)==a1&&(x/gcd(x,b0)*b0==b1))//乘以b0放在后面,否则运算结果溢出,尤其是第2个输入用例   
                         cnt++;  
                 }   
                 if((b1/x)%a1==0&&x*x!=b1)//如果b1/x确实是a1的倍数,并且x与b1/x不相等   
                 {   
                     if(gcd(b1/x,a0)==a1&&(b1/x/gcd(b1/x,b0)*b0==b1))//乘以b0放在后面,否则运算结果溢出   
                     cnt++;  
                 }   
             }  
         }  
         printf("%d\n",cnt);   
     }  
     return 0;  
 }  

算法训练 接水问题

问题描述
  学校里有一个水房,水房里一共装有m 个龙头可供同学们打开水,每个龙头每秒钟的 供水量相等,均为1。 现在有n 名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从1 到n 编号,i 号同学的接水量为wi。接水开始时,1 到m 号同学各占一个水龙头,并同时打 开水龙头接水。当其中某名同学j 完成其接水量要求wj 后,下一名排队等候接水的同学k 马上接替j 同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即 j 同学第x 秒结束时完成接水,则k 同学第x+1 秒立刻开始接水。若当前接水人数n’不足m, 则只有n’个龙头供水,其它m?n’个龙头关闭。 现在给出n 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。
输入格式
  第1 行2 个整数n 和m,用一个空格隔开,分别表示接水人数和龙头个数。 第2 行n 个整数w1、w2、……、wn,每两个整数之间用一个空格隔开,wi 表示i 号同 学的接水量。
输出格式
  输出只有一行,1 个整数,表示接水所需的总时间。
样例输入
5 3
4 4 1 2 1
样例输出
4
样例输入
8 4
23 71 87 32 70 93 80 76
样例输出
163
输入输出样例 1 说明
  第1 秒,3 人接水。第1 秒结束时,1、2、3 号同学每人的已接水量为1,3 号同学接完
  水,4 号同学接替3 号同学开始接水。
  第2 秒,3 人接水。第2 秒结束时,1、2 号同学每人的已接水量为2,4 号同学的已接
  水量为1。
  第3 秒,3 人接水。第3 秒结束时,1、2 号同学每人的已接水量为3,4 号同学的已接
  水量为2。4 号同学接完水,5 号同学接替4 号同学开始接水。
  第4 秒,3 人接水。第4 秒结束时,1、2 号同学每人的已接水量为4,5 号同学的已接
  水量为1。1、2、5 号同学接完水,即所有人完成接水。
  总接水时间为4 秒。
数据规模和约定
  1 ≤ n ≤ 10000,1 ≤m≤ 100 且m≤ n;
  1 ≤ wi ≤ 100。

 #include <stdio.h>  
 #include <stdlib.h>  
 #include <malloc.h>  
   run this program using the console pauser or add your own getch, system("pause") or input loop    
 int max(int *p,int n)  
 {  
     int Max = p[0];  
     int i;  
     for(i=1;i<n;i++)  
     {  
         if(p[i]>Max)  
                Max = p[i];    
     }     
     return Max;  
 }  
 int main(int argc, char *argv[]) {  
     int m,n;  //m个水龙头 n个同学  
     int i,j,second,min,num;  
     int *p;  
     scanf("%d%d",&n,&m);  
     p = (int *)malloc(n*sizeof(int));  
     if( NULL == p )  
     {  
         printf("no enough memory!\n");  
         return 0;  
     }  
     for(i=0;i<n;i++)  
     {  
         scanf("%d",&p[i]);      
     }  
     if( n<=m)  
     {         
         printf("%d",max(p,n));  
     }  
     if( n>m)  
     {  
         second = 0;  
         num = 0;  
         while(n>m)  
         {  
             min = p[0];   
             j = 0;  
             for(i=0;i<m;i++)  
             {    
                if(min>p[i])  
                {  
                     min = p[i];           
                     j=i;  
                }  
             }     
             for(i=0;i<m;i++)  
             {  
                 p[i] -= min;  
             }  
             second += min;  
             p[j]=p[m+num];  //第m+num同学取代取完水的同学   
             num++;  
             n--;//取水同学减少一个   
             if( n<=m)  
             {             
              printf("%d",max(p,n)+second);  
             }  
        }  
     }  
     return 0;  
 }  

算法训练 数组排序去重

问题描述
  输入10个整数组成的序列,要求对其进行升序排序,并去掉重复元素。
输入格式
  10个整数。
输出格式
  多行输出,每行一个元素。
样例输入
2 2 3 3 1 1 5 5 5 5
样例输出
1
2
3
5

 #include <stdio.h>  
 #include <stdlib.h>  
 int A[10];  
 int com(const void *a, const void *b)  
 {  
     return *(int*)a-*(int*)b;  
 }  
 int main()  
 {  
     int i;  
     int last;  
     for(i=0;i<10;i++)  
         scanf("%d",&A[i]);  
     qsort(A,10,4,com);  
     for(i=0;i<10;i++)  
         if(i)  
         {  
             if(last!=A[i])  
             {  
                 printf("%d\n",A[i]);  
                 last=A[i];  
             }  
         }  
         else  
         {  
             printf("%d\n",A[i]);  
             last=A[i];  
         }  
     return 0;  
 }  

算法训练 会议中心

会议中心  Siruseri政府建造了一座新的会议中心。许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议。
  对于一个客户而言,仅当在开会时能够独自占用整个会堂,他才会租借会堂。会议中心的销售主管认为:最好的策略应该是将会堂租借给尽可能多的客户。显然,有可能存在不止一种满足要求的策略。
  例如下面的例子。总共有4个公司。他们对租借会堂发出了请求,并提出了他们所需占用会堂的起止日期(如下表所示)。

开始日期
结束日期
公司1
4
9
公司2
9
11
公司3
13
19
公司4
10
17

  上例中,最多将会堂租借给两家公司。租借策略分别是租给公司1和公司3,或是公司2和公司3,也可以是公司1和公司4。注意会议中心一天最多租借给一个公司,所以公司1和公司2不能同时租借会议中心,因为他们在第九天重合了。
  销售主管为了公平起见,决定按照如下的程序来确定选择何种租借策略:首先,将租借给客户数量最多的策略作为候选,将所有的公司按照他们发出请求的顺序编号。对于候选策略,将策略中的每家公司的编号按升序排列。最后,选出其中字典序最小[1]的候选策略作为最终的策略。
  例中,会堂最终将被租借给公司1和公司3:3个候选策略是{(1,3),(2,3),(1,4)}。而在字典序中(1,3)<(1,4)<(2,3)。
  你的任务是帮助销售主管确定应该将会堂租借给哪些公司。
输入格式
  输入的第一行有一个整数N,表示发出租借会堂申请的公司的个数。第2到第N+1行每行有2个整数。第i+1行的整数表示第i家公司申请租借的起始和终止日期。对于每个公司的申请,起始日期为不小于1的整数,终止日期为不大于109的整数。
输出格式
  输出的第一行应有一个整数M,表示最多可以租借给多少家公司。第二行应列出M个数,表示最终将会堂租借给哪些公司。
数据规模和约定
  对于50%的输入,N≤3000。在所有输入中,N≤200000。
样例输入
4
4 9
9 11
13 19
10 17
样例输出
2
1 3

[1] 字典序指在字典中排列的顺序,如果序列l1是序列l2的前缀,或者对于l1和l2的第一个不同位置j,l1[j]<l2[j],则l1比l2小。

 //C++  
 #include <cstdio>  
 #include <cstdlib>  
 #include <algorithm>  
 #include <string>  
 #include <set>  
 const char fi[] = "convention.in";  
 const char fo[] = "convention.out";  
 const int maxN = 200010;  
 const int MAX = 0x3f3f3f3f,MIN = ~MAX;  
 struct Seg  
 {  
     int L,R;  
     Seg()  
     {  
     }  
     Seg(int L,int R): L(L),R(R)  
     {  
     }  
     bool operator<(const Seg &b) const  
     {  
         return L < b.L || L == b.L && R < b.R;  
     }  
 };  
 std::set <Seg> S;  
 std::set <Seg>::iterator iter;  
 Seg req[maxN],seg[maxN],tmp[maxN];  
 int tab[maxN << 1],next[20][maxN << 1];  
 int n,cnt,Lim = 1,logLim;  
 void init_file()  
 {  
     return;  
 }  
 inline int getint()  
 {  
     int res = 0; char tmp;  
     while(!isdigit(tmp = getchar()));  
     do res = (res << 3) + (res << 1) + tmp - '0';  
     while(isdigit(tmp = getchar()));  
     return res;  
 }  
 void readdata()  
 {  
     n = getint();  
     for(int i = 0; i < n; ++i)  
     {  
         int L = getint(),R = getint();  
         req[i] = Seg(L,R);  
         tab[i << 1] = L;  
         tab[(i << 1) + 1] = R;  
     }  
     return;  
 }  
 int plc(int x)  
 {  
     for(int L = 0,R = Lim - 1; L < R + 1;)  
     {  
         int Mid = (L + R) >> 1;  
         if(x == tab[Mid]) return Mid + 1;  
         if(x < tab[Mid]) R = Mid - 1;  
         else L = Mid + 1;  
     }  
 }  
 bool cmp(const Seg &a,const Seg &b)  
 {  
     return a.R < b.R || a.R == b.R && a.L > b.L;  
 }  
 void discrete()  
 {  
     std::sort(tab,tab + (n << 1));  
     for(int i = 1; i < n << 1; ++i)  
         if(tab[i] != tab[i - 1])  
             tab[Lim++] = tab[i];  
     for(int i = 0; i < n; ++i)  
         tmp[i] = req[i] = Seg(plc(req[i].L),  
         plc(req[i].R));  
     std::sort(tmp,tmp + n,cmp);  
     //这里必须要用一个临时数组,  
     //保证左界右界同时单调递增。  
     int p = 0; seg[cnt++] = tmp[0];  
     for(int i = 1; i < n; ++i)  
         if(tmp[i].L > tmp[p].L)  
             seg[cnt++] = tmp[p = i];  
     return;  
 }  
 void next_set()  
 {  
     int p = cnt; next[0][Lim + 1] = MAX;  
     for(int j = Lim; j; --j)  
         if(p > -1 && j == seg[p - 1].L)  
             next[0][j] = seg[--p].R + 1;  
         else next[0][j] = next[0][j + 1];  
         for(int i = 0;; ++i)  
         {  
             bool flag = 0;  
             next[i + 1][Lim + 1] = MAX;  
             for(int k = 1; k < Lim + 1; ++k)  
             {  
                 if(next[i][k] == MAX)  
                     next[i + 1][k] = MAX;  
                 else next[i + 1][k] = next[i][next[i][k]];  
                 if(next[i + 1][k] < MAX) flag = 1;  
             }  
             if(!flag)  
             {  
                 logLim = i; break;  
             }  
         }  
         return;  
 }  
 int max_time(int L,int R)  
 {  
     if(L > R++) return 0;  
     int ans = 0,p = L;  
     for(int i = logLim; i > -1 && p < R; --i)  
         if(next[i][p] <= R)  
         {  
             p = next[i][p]; ans += 1 << i;  
         }  
     return ans;  
 }  
 bool query(int i)  
 {  
     int L = req[i].L,R = req[i].R;  
     iter = S.lower_bound(Seg(L,MAX));  
     if(iter-- == S.begin()) return 0;  
     if(iter->L > L || iter->R < R)  
         return 0;  
     int L1 = iter->L,R1 = iter->R;  
     if(max_time(L1,L - 1)  
         + max_time(R + 1,R1)  
         + 1 < max_time(L1,R1))  
         //这里要满足放进去过后不影响总的答案。  
         return 0;  
     S.erase(iter);  
     if(L1 < L) S.insert(Seg(L1,L - 1));  
     if(R < R1) S.insert(Seg(R + 1,R1));  
     return 1;  
 }  
 void work()  
 {  
     printf("%d\n",max_time(1,Lim));  
     S.insert(Seg(1,Lim));  
     for(int i = 0; i < n; ++i)  
         if(query(i))  
             printf("%d ",i + 1);  
     printf("\n");  
     return;  
 }  
 int main()  
 {  
     init_file();  
     readdata();  
     discrete();  
     next_set();  
     work();  
     return 0;  
 }  
阅读更多

没有更多推荐了,返回首页