贪心算法专题复习
codevs 1098 均分纸牌
2002年NOIP全国联赛提高组
有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌,然后移动。
移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
例如 N=4,4 堆纸牌数分别为:
① 9 ② 8 ③ 17 ④ 6
移动3次可达到目的:
从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3 张牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。
第一行N(N 堆纸牌,1 <= N <= 100)
第二行A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)
输出至屏幕。格式为:
所有堆均达到相等时的最少移动次数。‘
4
9 8 17 6
3
e
【算法分析】
如果你想到把每堆牌的张数减去平均张数,题目就变成移动正数,加到负数中,使大家都变成0,那就意味着成功了一半!拿例题来说,平均张数为10,原张数9,8,17,6,变为-1,-2,7,-4,其中没有为0的数,我们从左边出发:要使第1堆的牌数-1变为0,只须将-1张牌移到它的右边(第2堆)-2中;结果是-1变为0,-2变为-3,各堆牌张数变为0,-3,7,-4;同理:要使第2堆变为0,只需将-3移到它的右边(第3堆)中去,各堆牌张数变为0,0,4,-4;要使第3堆变为0,只需将第3堆中的4移到它的右边(第4堆)中去,结果为0,0,0,0,完成任务。每移动1次牌,步数加1。也许你要问,负数张牌怎么移,不违反题意吗?其实从第i堆移动-m张牌到第i+1堆,等价于从第i+1堆移动m张牌到第i堆,步数是一样的。 如果张数中本来就有为0的,怎么办呢?如0,-1,-5,6,还是从左算起(从右算起也完全一样),第1堆是0,无需移牌,余下与上相同;再比如-1,-2,3,10,-4,-6,从左算起,第1次移动的结果为0,-3,3,10,-4,-6;第2次移动的结果为0,0,0,10,-4,-6,现在第3堆已经变为0了,可节省1步,余下继续。
1 #include<iostream> 2 using namespace std; 3 #define N 110 4 #include<cstdio> 5 int a[N],n,aver=0,ans=0; 6 int main() 7 { 8 scanf("%d",&n); 9 for(int i=1;i<=n;++i) 10 scanf("%d",&a[i]),aver+=a[i]; 11 aver/=n; 12 for(int i=1;i<=n;++i) a[i]-=aver; 13 int i=1,j=n; 14 while(a[i]==0&&i<n) i++; 15 while(a[j]==0&&j>0) j--; 16 while(i<j) 17 { 18 a[i+1]+=a[i]; 19 a[i]=0; 20 ans++; 21 i++; 22 while(a[i]==0&&i<j)i++; 23 } 24 printf("%d\n",ans); 25 return 0; 26 }
codevs 4906 删数问题
键盘输入一个高精度的正整数N,去掉其中任意S个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N 和S,寻找一种方案使得剩下的数字组成的新数最小。
输入数据均不需要判错。
输出组成的新的正整数。(N不超过240位)
第一行,输入一正整数N(N<=10240),表示要删的数;
第二行,输入一正整数S,表示删去的个数,当然S小于N的位数。
仅一行,输出删数后形成的最小数。(不能有多余空格)
【1】
5768
1
【2】
8796542
4
【1】
568
【2】
542
1<=N<=10^240
1<=S<=239
注意开头的0应略去
【算法分析】
由于正整数n的有效数位为240位,所以很自然地采用字符串类型存贮n。那么如何决定哪s位被删除呢?是不是最大的s个数字呢?显然不是,大家很容易举出一些反例。为了尽可能逼近目标,我们选取的贪心策略为:每一步总是选择一个使剩下的数最小的数字删去,即按高位到低位的顺序搜索,若各位数字递增,则删除最后一个数字;否则删除第一个递减区间的首字符,这样删一位便形成了一个新数字串。然后回到串首,按上述规则再删下一个数字。重复以上过程s次为止,剩下的数字串便是问题的解了。
例如:n=175438 s=4 删数的过程如下: n=175438 //删掉7 15438 //删掉5 1438 //删掉4 138 //删掉8 13 //解为13 这样,删数问题就与如何寻找递减区间首字符这样一个简单的问题对应起来。不过还要注意一个细节性的问题,就是可能会出现字符串串首有若干0的情况,甚至整个字符串都是0的情况。
1 /* 2 思路不是很难,但是需要注意的细节很多的,很容易就错了。 3 例如:在从高位向低位需寻找时,每找到一个就跳出一次,否则就不能保证找到的是s次了。 4 还有输入的数有前导0,结果有前导0的情况,都应该考虑到。 5 还有整个数列都是一个递增的数列,那么这是就要删掉最后一个了。 6 */ 7 #define N 250 8 #include<iostream> 9 using namespace std; 10 #include<cstdio> 11 #include<cstring> 12 char shu[N],s; 13 int main() 14 { 15 scanf("%s%d",shu+1,&s); 16 int len=strlen(shu+1); 17 shu[len+1]='0';//方便删去最后一个数字的情况。 18 for(int i=1;i<=s;++i) 19 { 20 for(int j=1;j<=len;++j) 21 { 22 if(shu[j]>shu[j+1]) 23 { 24 for(int k=j;k<=len;++k) 25 shu[k]=shu[k+1]; 26 len--; 27 break; 28 } 29 } 30 } 31 int k=1; 32 while(shu[k]=='0'&&k<len)k++; 33 for(int i=k;i<=len;++i) 34 printf("%c",shu[i]); 35 return 0; 36 }
codevs 1044 拦截导弹
1999年NOIP全国联赛提高组
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)
输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
389 207 155 300 299 170 158 65
6
2
导弹的高度<=30000,导弹个数<=20
1 /* 2 好长时间没搞动态规划了,手都生了。~~ 3 */ 4 #define N 110 5 #include<iostream> 6 using namespace std; 7 #include<cstdio> 8 int xl[N],n,zb[N],f[N]={0},ans=0; 9 int main() 10 { 11 int n=1; 12 while(scanf("%d",&xl[n])==1) n++; 13 n--; 14 for(int i=1;i<=n;++i) 15 f[i]=1; 16 ans=1; 17 for(int i=2;i<=n;++i) 18 for(int j=1;j<i;++j) 19 if(xl[j]>=xl[i]) 20 { 21 f[i]=max(f[j]+1,f[i]); 22 ans=max(ans,f[i]); 23 } 24 printf("%d\n",ans); 25 int nowt=1; 26 zb[1]=xl[1]; 27 for(int i=2;i<=n;++i) 28 { 29 int j=0,minn=(1<<31)-1; 30 for(int k=1;k<=nowt;++k) 31 { 32 if(zb[k]<minn&&zb[k]>=xl[i]) 33 { 34 minn=zb[k]; 35 j=k; 36 } 37 } 38 if(j==0) 39 { 40 nowt++; 41 zb[nowt]=xl[i]; 42 } 43 else{ 44 zb[j]=xl[i]; 45 } 46 } 47 printf("%d",nowt); 48 return 0; 49 }
codevs 2610 活动选择(选择不相交区间问题)
假设有一个需要使用某一资源的n(n≤1000)个活动组成的集合S,S={1,…,n}。该资源一次只能被一个活动占有,每一个活动有一个开始时间bi和结束时间ei(bi≤ei)。若bi>ej或者bj>ei,则活动i和活动j兼容。
你的任务是是:选择由互相兼容的活动组成的最大集合。
共n+1行,其中第1行为n,第2行到第n+1行表示n个活动的开始时间和结束时间(中间用一个空格隔开),格式为:
n
b1 e1
…….
bn en
共有两行,第1行为满足要求的活动占用的时间t,第2行为最大集合中的活动序号,每个序号之间用一个空格隔开。
11
3 5
1 4
12 14
8 12
0 6
8 11
6 10
5 7
3 8
5 9
2 13
14
2 3 6 8
数据范围不大,不用考虑。
【算法分析】 • 算法模型:给n个开区间(begini,endi), 选择尽量多的区间, 使得两两不交。 • 做法: 首先按照end1<=end2<…<=endn的顺序排序,依次考虑各个活动, 如果没有和已经选择的活动冲突, 就选; 否则就不选。 • 正确性: 如果不选end1, 假设第一个选择的是endi,则如果endi和end1不交叉则多选一个end1更划算; 如果交叉则把endi换成end1不影响后续选择。
1 #include<algorithm> 2 #define N 1005 3 #include<iostream> 4 using namespace std; 5 #include<cstdio> 6 struct Tim{ 7 int x,y,bh; 8 }tim[N]; 9 int n,sum=0,ans[N]={0}; 10 void input() 11 { 12 scanf("%d",&n); 13 for(int i=1;i<=n;++i) 14 { 15 scanf("%d%d",&tim[i].x,&tim[i].y); 16 tim[i].bh=i; 17 } 18 } 19 bool cmp(Tim A,Tim B) 20 { 21 if(A.y<B.y) return true; 22 if(A.y==B.y&&A.x>B.x) return true; 23 return false; 24 } 25 int main() 26 { 27 input(); 28 sort(tim+1,tim+n+1,cmp); 29 int nowt=1; 30 ans[++ans[0]]=tim[1].bh; 31 sum+=tim[1].y-tim[1].x+1; 32 for(int i=2;i<=n;++i) 33 { 34 if(tim[i].x>tim[nowt].y) 35 { 36 ans[++ans[0]]=tim[i].bh; 37 sum+=tim[i].y-tim[i].x+1; 38 nowt=i; 39 } 40 } 41 printf("%d\n",sum); 42 sort(ans+1,ans+ans[0]+1); 43 for(int i=1;i<=ans[0];++i) 44 printf("%d ",ans[i]); 45 return 0; 46 }
【例8】整数区间 请编程完成以下任务:
1.从文件中读取闭区间的个数及它们的描述;
2.找到一个含元素个数最少的集合,使得对于每一个区间,都至少有一个整数属于该集合,输出该集合的元素个数。 【输入】 首行包括区间的数目n,1<=n<=10000,接下来的n行,每行包括两个整数a,b,被一空格隔开,0<=a<=b<=10000,它们是某一个区间的开始值和结束值。 【输出】 第一行集合元素的个数,对于每一个区间都至少有一个整数属于该区间,且集合所包含元素数目最少。
【样例输入】 4
3 6
2 4
0 2
4 7
【样例输出】
2
【算法分析】 •算法模型:
给n个闭区间[ai,bi], 在数轴上选尽量少的点,使每个区间内至少有一个点。
•算法:首先按b1<=b2<=...<=bn排序。每次标记当前区间的右端点x,并右移当前区间指针,
直到当前区间不包含x,再重复上述操作。
•如下图,如果选灰色点,移动到黑色点更优。
1 #define N 10010 2 #include<iostream> 3 using namespace std; 4 #include<cstdio> 5 #include<algorithm> 6 int n; 7 struct Qj{ 8 int a,b; 9 }qj[N]; 10 int nowt; 11 void input() 12 { 13 scanf("%d",&n); 14 for(int i=1;i<=n;++i) 15 scanf("%d%d",&qj[i].a,&qj[i].b); 16 } 17 bool cmp(Qj A,Qj B) 18 { 19 if(A.b<B.b) return true; 20 if(A.b==B.b&&A.a<B.a) return true; 21 return false; 22 } 23 int main() 24 { 25 input(); 26 int sum=0; 27 sort(qj+1,qj+n+1,cmp); 28 nowt=qj[1].b; 29 sum=1; 30 for(int i=2;i<=n;++i) 31 { 32 if(nowt>=qj[i].a) continue; 33 nowt=qj[i].b; 34 sum++; 35 } 36 printf("%d\n",sum); 37 return 0; 38 }
最小区间覆盖问题:
UVa 10020 (最小区间覆盖) Minimal coverage
题目:http://vjudge.net/problem/UVA-10020
数轴上有n个闭区间[ai,bi],选择尽量少的区间覆盖一条指定的线段[s,t]。
思路:贪心,具体见刘汝佳白书P154。把各区间按照a从小到大排序。如果区间1的起点不是s,无解,
否则选择起点在s的最长区间。选择此区间[ai,bi]后,新的起点设置为bi,然后经过依次扫描之后就可以得出最小的线段数。
并用另一个数组储存路径,输出前别忘记排序。
Given several segments of line (int the X axis) with coordinates [Li
, Ri]. You are to choose the minimal
amount of them, such they would completely cover the segment [0, M].
Input
The first line is the number of test cases, followed by a blank line.
Each test case in the input should contains an integer M (1 ≤ M ≤ 5000), followed by pairs “Li Ri”
(|Li|, |Ri| ≤ 50000, i ≤ 100000), each on a separate line. Each test case of input is terminated by pair
‘0 0’.
Each test case will be separated by a single line.
Output
For each test case, in the first line of output your programm should print the minimal number of line
segments which can cover segment [0, M]. In the following lines, the coordinates of segments, sorted
by their left end (Li), should be printed in the same format as in the input. Pair ‘0 0’ should not be
printed. If [0, M] can not be covered by given line segments, your programm should print ‘0’ (without
quotes).
Print a blank line between the outputs for two consecutive test cases.
Sample Input
2
1
-1 0
-5 -3
2 5
0 0
1
-1 0
0 1
0 0
Sample Output
0
1
0 1
1 #define N 100010 2 #include<iostream> 3 #include<algorithm> 4 using namespace std; 5 #include<cstdio> 6 int T,M; 7 struct Qj{ 8 int l,r; 9 }qj[N]; 10 int qjs=0; 11 int ans[N]; 12 void input() 13 { 14 scanf("%d",&M); 15 qjs=1; 16 while(scanf("%d%d",&qj[qjs].l,&qj[qjs].r)==2) 17 { 18 if(qj[qjs].l==0&&qj[qjs].r==0) break; 19 qjs++; 20 } 21 qjs--; 22 } 23 bool cmp(int a,int b) 24 { 25 return qj[a].l<qj[b].l; 26 } 27 int main() 28 { 29 scanf("%d",&T); 30 while(T--) 31 { 32 input(); 33 ans[0]=0; 34 int nowl=0,nowr=0; 35 bool flag=true; 36 while(nowr<M) 37 { 38 int j=0; 39 for(int i=1;i<=qjs;++i) 40 { 41 if(qj[i].l<=nowl&&qj[i].r>nowr) 42 { 43 nowr=qj[i].r; 44 j=i; 45 } 46 } 47 if(j==0) 48 { 49 flag=false; 50 break; 51 } 52 else{ 53 ans[++ans[0]]=j; 54 nowl=nowr=qj[j].r; 55 } 56 } 57 if(!flag)printf("0\n\n"); 58 else { 59 printf("%d\n",ans[0]); 60 sort(ans+1,ans+ans[0]+1,cmp); 61 for(int i=1;i<=ans[0];++i) 62 printf("%d %d\n",qj[ans[i]].l,qj[ans[i]].r); 63 printf("\n"); 64 } 65 } 66 return 0; 67 }
背包类相关问题(只有2个物品)
元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得 的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品, 并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。
你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。
输入输出格式
输入格式:
输入文件group.in包含n+2行:
第1行包括一个整数w,为每组纪念品价格之和的上上限。
第2行为一个整数n,表示购来的纪念品的总件数G
第3~n+2行每行包含一个正整数Pi (5 <= Pi <= w)w表示所对应纪念品的价格。
输出格式:
输出文件group.out仅一行,包含一个整数,即最少的分组数目。
输入输出样例
100 9 90 20 20 30 50 60 70 80 90
6
说明
50%的数据满足:1<=n<=15
100%的数据满足:1<=n<=30000,80<=w<=200
1 #include<iostream> 2 using namespace std; 3 #include<cstdio> 4 #define N 30005 5 int w,n,mon[N],ans=0; 6 int zc[N]; 7 void input() 8 { 9 scanf("%d%d",&w,&n); 10 for(int i=1;i<=n;++i) 11 { 12 scanf("%d",&mon[i]); 13 } 14 } 15 void Gbsort(int l,int r) 16 { 17 if(l==r) return; 18 int mid=(l+r)>>1; 19 Gbsort(l,mid); 20 Gbsort(mid+1,r); 21 int i=l,s=l,j=mid+1; 22 while(i<=mid&&j<=r) 23 { 24 if(mon[i]<mon[j]) 25 { 26 zc[s]=mon[i]; 27 i++;s++; 28 } 29 else{ 30 zc[s]=mon[j]; 31 j++;s++; 32 } 33 } 34 while(i<=mid) 35 { 36 zc[s]=mon[i]; 37 s++;i++; 38 } 39 while(j<=r) 40 { 41 zc[s]=mon[j]; 42 j++;s++; 43 } 44 for(i=l;i<=r;++i) 45 mon[i]=zc[i]; 46 } 47 int main() 48 { 49 input(); 50 Gbsort(1,n); 51 int i=1,j=n; 52 while(i<j) 53 { 54 if(mon[i]+mon[j]<=w) 55 { 56 ans++;i++;j--; 57 } 58 else 59 { 60 ans++;j--; 61 } 62 } 63 if(i==j) ans++; 64 printf("%d\n",ans); 65 return 0; 66 }
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。
输入输出格式
输入格式:
输入文件fruit.in包括两行,第一行是一个整数n(1<=n<=10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。
输出格式:
输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。
输入输出样例
3 1 2 9
15
说明
对于30%的数据,保证有n<=1000:
对于50%的数据,保证有n<=5000;
对于全部的数据,保证有n<=10000。
1 /* 2 哈夫曼树:因为有些值会被重复计算,那么要使得结果最小,就要使数值小的被重复加,数值大的尽量少被重复加。使用STL会更快。 3 */ 4 #include<iostream> 5 #include<cstdio> 6 #include<queue> 7 #include<vector> 8 using namespace std; 9 int n; 10 struct cmp{/*将优先队列改为从小到大*/ 11 bool operator ()(const int a,const int b) 12 const{ return a>b;} 13 }; 14 priority_queue<int,vector<int>,cmp>Q; 15 int x; 16 int main() 17 { 18 scanf("%d",&n); 19 for(int i=1;i<=n;++i) 20 { 21 scanf("%d",&x); 22 Q.push(x); 23 } 24 int ans=0; 25 while(n>1) 26 { 27 int a=Q.top();Q.pop();n--; 28 int b=Q.top();Q.pop();n--; 29 ans+=a+b; 30 Q.push(a+b);n++; 31 } 32 printf("%d\n",ans); 33 return 0; 34 }
最新讨论
题目背景
此处省略maxint+1个数
题目描述
在以后的若干天里戴维将学习美元与德国马克的汇率。编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后能获得最高可能的价值。
输入输出格式
输入格式:
输入文件的第一行是一个自然数N,1≤N≤100,表示戴维学习汇率的天数。
接下来的N行中每行是一个自然数A,1≤A≤1000。第i+1行的A表示预先知道的第i+1天的平均汇率,在这一天中,戴维既能用100美元买A马克也能用A马克购买100美元。
输出格式:
输出文件的第一行也是唯一的一行应输出要求的钱数(单位为美元,保留两位小数)。
注意:考虑到实数算术运算中进位的误差,结果在正确结果0.05美元范围内的被认为是正确的,戴维必须在最后一天结束之前将他的钱都换成美元。
输入输出样例
5 400 300 500 300 250
266.66
说明
样例解释 (无需输出)
Day 1 ... changing 100.0000 美元= 400.0000 马克
Day 2 ... changing 400.0000 马克= 133.3333 美元
Day 3 ... changing 133.3333 美元= 666.6666 马克
Day 5 ... changing 666.6666 马克= 266.6666 美元
1 /* 2 思路:使用两个变量,始终存着到当前这一天的最大美元数,最大马克数,只有两条路同时进行才能保证正确。 3 */ 4 #define N 105 5 #include<iostream> 6 using namespace std; 7 #include<cstdio> 8 double tim[N],nowmei,nowma,befmei,befma; 9 int n; 10 int main() 11 { 12 scanf("%d",&n); 13 for(int i=1;i<=n;++i) 14 cin>>tim[i]; 15 befmei=100;befma=100*tim[1]/100; 16 for(int i=2;i<=n;++i) 17 { 18 nowma=befma; 19 nowmei=befmei; 20 nowmei=max(befma/tim[i]*100,nowmei); 21 nowma=max(befmei*tim[i]/100,nowma); 22 befma=nowma; 23 befmei=nowmei; 24 } 25 int anss=nowmei*100; 26 /*printf是有四舍五入功能的*/ 27 //printf("%0.2lf\n",anss*1.0/100.0);/*这个是能符合样例的,舍去所有的小数,但是会有一部分数据不能过。*/ 28 printf("%.2lf\n",nowmei);/*直接这样输出就可以了。*/ 29 return 0; 30 }
题目描述
设有n个正整数 (n<=20), 将它们连接成一排, 组成一个最大的多位整数.
例如: n=3时, 3个整数13, 312, 343连接成的最大整数为: 34331213
又如: n=4时, 4个整数7,13,4,246连接成的最大整数为: 7424613
输入输出格式
输入格式:
n n个数
输出格式:
连接成的多位数
输入输出样例
3 13 312 343 4 7 13 4 246
34331213 7424613
1 /* 2 明显的:应该是大的数(仅只一位)尽量往高位上放。但是如果仅仅这样逐位判断每一个数的话,是有可能不准确的。比如312和31,应是31312而不是31231.一个比较简单的方法就是把任意两个数都接在一起,比较两个数前后接的大小,如果AB》BA,那么在最终的答案里也一定是A在B之前的。 3 */ 4 #include<algorithm> 5 #include<iostream> 6 using namespace std; 7 #include<cstdio> 8 #include<cstring> 9 #define N 25 10 struct Zs{ 11 char a[21]; 12 int len; 13 }zs[N]; 14 int n; 15 int Bcmp(char *a,char *b,int len) 16 { 17 for(int i=1;i<=len;++i) 18 { 19 if(a[i]>b[i]) return 1; 20 if(a[i]<b[i]) return -1; 21 } 22 return 0; 23 } 24 bool cmp(Zs A,Zs B) 25 { 26 char AB[30]={0},BA[30]={0}; 27 for(int i=1;i<=A.len;++i) 28 AB[i]=A.a[i]; 29 for(int i=A.len+1;i<=A.len+B.len;++i) 30 AB[i]=B.a[i-A.len]; 31 for(int i=1;i<=B.len;++i) 32 BA[i]=B.a[i]; 33 for(int i=B.len+1;i<=A.len+B.len;++i) 34 BA[i]=A.a[i-B.len]; 35 if(Bcmp(AB,BA,A.len+B.len)>=0) return true; 36 return false; 37 } 38 int main() 39 { 40 scanf("%d",&n); 41 for(int i=1;i<=n;++i) 42 { 43 scanf("%s",zs[i].a+1); 44 zs[i].len=strlen(zs[i].a+1); 45 } 46 sort(zs+1,zs+n+1,cmp); 47 for(int i=1;i<=n;++i) 48 printf("%s",zs[i].a+1); 49 return 0; 50 }
题目描述
某工厂生产一批棍状零件,每个零件都有一定的长度(Li)和重量(Wi)。现在为了加工需要,要将它们分成若干组,使每一组的零件都能排成一个长度和重量都不下降(若i<j,则Li<=Lj,Wi<=Wj)的序列。请问至少要分成几组?
输入输出格式
输入格式:
第一行为一个整数N(N<=1000),表示零件的个数。第二行有N对正整数,每对正整数表示这些零件的长度和重量,长度和重量均不超过10000。
输出格式:
仅一行,即最少分成的组数。
输入输出样例
5 8 4 3 8 2 3 9 7 3 5
2
1 /* 2 这道题是我想麻烦了,排完序后,根本没有必要来DP求最长不下降子序列(而且也不对,比如当两个序列一样长的时候,就非常难处理了),
直接用和“导弹拦截”差不多的贪心算法即可解决。对于每一个零件,尽可能把把他接在比他稍稍小一点点或者相等的序列后面,这样就能保证最优了。 3 */ 4 #include<cstring> 5 #define N 1005 6 #include<iostream> 7 using namespace std; 8 #include<cstdio> 9 struct Lj{ 10 int l,w,xh; 11 }lj[N],lj1[N]; 12 int fz[N],n,f[N],pre[N]; 13 bool is_lj[N]; 14 void input() 15 { 16 scanf("%d",&n); 17 for(int i=1;i<=n;++i) 18 { 19 scanf("%d%d",&lj[i].l,&lj[i].w); 20 lj[i].xh=i; 21 } 22 } 23 void Gbsort(int s,int t) 24 { 25 if(s==t) return; 26 int mid=(s+t)>>1; 27 Gbsort(s,mid); 28 Gbsort(mid+1,t); 29 int i=s,j=mid+1,k=s; 30 while(i<=mid&&j<=t) 31 { 32 if(lj[i].l<lj[j].l||(lj[i].l==lj[j].l&&lj[i].w<=lj[j].w)) 33 { 34 lj1[k]=lj[i]; 35 i++;k++; 36 } 37 else{ 38 lj1[k]=lj[j]; 39 j++;k++; 40 } 41 } 42 while(i<=mid) 43 { 44 lj1[k]=lj[i]; 45 i++;k++; 46 } 47 while(j<=t) 48 { 49 lj1[k]=lj[j]; 50 j++;k++; 51 } 52 for(k=s;k<=t;++k) 53 lj[k]=lj1[k]; 54 } 55 int main() 56 { 57 input(); 58 Gbsort(1,n); 59 memset(lj1,0,sizeof(lj1)); 60 int sum=1; 61 fz[1]=lj[1].w; 62 for(int i=2;i<=n;++i) 63 { 64 int maxx=-1,k=0; 65 for(int j=1;j<=sum;++j) 66 { 67 if(fz[j]<=lj[i].w&&fz[j]>maxx) 68 { 69 k=j;maxx=fz[j]; 70 } 71 } 72 if(k==0) 73 { 74 sum++; 75 fz[sum]=lj[i].w; 76 } 77 else{ 78 fz[k]=lj[i].w; 79 } 80 } 81 printf("%d\n",sum); 82 return 0; 83 }