版权声明:本文为博主原创文章,未经博主允许不得转载。
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)
输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
7
389 207 155 300 299 170 158 65
6
2
导弹的高度<=30000,导弹个数<=20
- //朴素O(N^2)代码
- #include<iostream>
- #include<cstdio>
- using namespace std;
- int a[30000];
- int n=1,i,j,max1;
- int f[600000];
- int main()
- {
- while(scanf("%d",&a[n++])!=EOF);
- n--;
- f[1]=1;
- for(i=2;i<=n;i++){f[i]=1;
- for(j=1;j<=i-1;j++)
- if(f[j]+1>f[i] && a[j]>=a[i])f[i]=f[j]+1;
- }
- max1=0;
- for(i=1;i<=n;i++)if(f[i]>max1)max1=f[i];
- cout<<max1-1<<endl;
- f[1]=1;
- for(i=2;i<=n;i++){f[i]=1;
- for(j=1;j<=i-1;j++)
- if(f[j]+1>f[i] && a[j]<a[i])f[i]=f[j]+1;
- }
- max1=0;
- for(i=1;i<=n;i++)if(f[i]>max1)max1=f[i];
- cout<<max1;
- // cout<<f[n];
- return 0;
- }
- //O(nlogn)的代码,详情见鄙人博客的文章《单调队列优化LIS》
- #include <iostream>
- using namespace std;
- #include <cstdio>
- const int MaxN=100001;
- int n,i,top=0,x,stack[MaxN];
- int main(){
- cin>>n;
- stack[top]=-1;
- for(i=1;i<=n;i++){
- cin>>x;
- if(x>stack[top]){stack[++top]=x;}
- else
- {
- int low=0,high=top,mid;
- while(low<high){
- mid=(low+high)>>1;
- if(x>stack[mid])
- low=mid+1;
- else
- high=mid-1;
- }
- stack[low]=x;
- }
- }
- cout<<top;
- return 0;
- }
1.2.1 序列型动态规划例题——《线段覆盖2》(线段覆盖三部曲第二部)
数轴上有n条线段,线段的两端都是整数坐标,坐标范围在0~1000000,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合)且线段价值之和最大。
n<=1000
第一行一个整数n,表示有多少条线段。
接下来n行每行三个整数, ai bi ci,分别代表第i条线段的左端点ai,右端点bi(保证左端点<右端点)和价值ci。
输出能够获得的最大价值
3
1 2 1
2 3 2
1 3 4
4
数据范围
对于40%的数据,n≤10;
对于100%的数据,n≤1000;
0<=ai,bi<=1000000
0<=ci<=1000000
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- #include <cstdio>
- #include <cstdlib>
- #include <vector>
- #include <queue>
- #include <list>
- #include <deque>
- #include <string>
- using namespace std;
- const int MaxN=1001;
- struct line{
- int L,R,val;
- friend bool operator< (line a,line b){return a.R<b.R;}//重载运算符来排序
- }a[MaxN];
- int f[MaxN],n,i,j;
- //f(i)表示前i条线段任意选择且必选择第i条线段能获得的最大值
- int main(){
- scanf("%d",&n);
- for(i=1;i<=n;i++)
- scanf("%d%d%d",&a[i].L,&a[i].R,&a[i].val);
- sort(a+1,a+n+1);
- for(i=1;i<=n;i++)f[i]=a[i].val;
- for(i=2;i<=n;i++){
- for(j=1;j<i;j++)
- if(a[j].R<=a[i].L && f[j]+a[i].val>f[i])f[i]=a[i].val+f[j];
- if(f[i]>f[0])f[0]=f[i];
- }
- cout<<f[0];
- return 0;
- }
有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。
第一行一个整数n(n<=100)
第二行n个整数w1,w2...wn (wi <= 100)
一个整数表示最小合并代价
4
4 1 1 4
18
- #include <iostream>
- using namespace std;
- #include <cstdio>
- const int MaxN=101;
- int n,s[MaxN],f[MaxN][MaxN];
- int i,j,k,r;
- int main(){
- cin>>n;
- for(i=1;i<=n;i++){
- cin>>s[i];
- s[i]+=s[i-1];
- }
- for(r=2;r<=n;r++)
- for(i=1;i<=n-r+1;i++){
- j=i+r-1;
- f[i][j]=0x7fffffff;
- for(k=i;k<j;k++)
- f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
- }
- cout<<f[1][n];
- return 0;
- }
在Mars星球上,每个Mars人都随身佩带着一串能量项链。在项链上有N颗能量珠。能量珠是一颗有头标记与尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记一定等于后一颗珠子的头标记。因为只有这样,通过吸盘(吸盘是Mars人吸收能量的一种器官)的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可以被吸盘吸收的能量。如果前一颗能量珠的头标记为m,尾标记为r,后一颗能量珠的头标记为r,尾标记为n,则聚合后释放的能量为m*r*n(Mars单位),新产生的珠子的头标记为m,尾标记为n。
需要时,Mars人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不同的,请你设计一个聚合顺序,使一串项链释放出的总能量最大。
例如:设N=4,4颗珠子的头标记与尾标记依次为(2,3) (3,5) (5,10) (10,2)。我们用记号⊕表示两颗珠子的聚合操作,(j⊕k)表示第j,k两颗珠子聚合后所释放的能量。则第4、1两颗珠子聚合后释放的能量为:
(4⊕1)=10*2*3=60。
这一串项链可以得到最优值的一个聚合顺序所释放的总能量为
((4⊕1)⊕2)⊕3)=10*2*3+10*3*5+10*5*10=710。
第一行是一个正整数N(4≤N≤100),表示项链上珠子的个数。第二行是N个用空格隔开的正整数,所有的数均不超过1000。第i个数为第i颗珠子的头标记(1≤i≤N),当i<N< span>时,第i颗珠子的尾标记应该等于第i+1颗珠子的头标记。第N颗珠子的尾标记应该等于第1颗珠子的头标记。
至于珠子的顺序,你可以这样确定:将项链放到桌面上,不要出现交叉,随意指定第一颗珠子,然后按顺时针方向确定其他珠子的顺序。
只有一行,是一个正整数E(E≤2.1*109),为一个最优聚合顺序所释放的总能量。
4
2 3 5 10
710
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- #include <cstdio>
- #include <cstdlib>
- #include <vector>
- #include <queue>
- #include <list>
- #include <deque>
- #include <string>
- using namespace std;
- const int MaxN=205;//n<=100,这里拉两倍
- int f[MaxN][MaxN];//f(i,j)表示[i,j]这段区间的最优值(将最后一个点放入前面)
- int a[MaxN];
- int n;
- int main(){
- int i,j,k,r;
- cin>>n;
- for(i=1;i<=n;i++)
- {
- cin>>a[i];
- a[i+n]=a[i];
- }
- for(j=2;j<(n<<1);j++)
- for(i=j-1;i>0 && j-i<n;i--)
- for(k=i;k<j;k++)
- f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+a[i]*a[k+1]*a[j+1]);
- int MAX=0;
- for(i=1;i<=n;i++)
- MAX=max(MAX,f[i][i+n-1]);
- cout<<MAX;
- return 0;
- }
今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:
设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。
同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:
有一个数字串:312, 当N=3,K=1时会有以下两种分法:
1) 3*12=36
2) 31*2=62
这时,符合题目要求的结果是:31*2=62
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。
程序的输入共有两行:
第一行共有2个自然数N,K(6≤N≤40,1≤K≤6)
第二行是一个长度为N的数字串。
结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。
4 2
1231
62
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- #include <cstdio>
- #include <cstdlib>
- #include <vector>
- #include <queue>
- #include <list>
- #include <deque>
- #include <string>
- using namespace std;
- typedef long long big;
- string s;
- int n,k;
- big f[41][7];//f(i,j)表示s中前i个数插入j个乘号所能得到的最大值
- big merge(int l,int r){
- big num=0;
- for(;l<=r;l++)num=num*10+s[l]-'0';
- return num;
- }
- int main(){
- cin>>n>>k;
- cin>>s;
- int i,j,ak;
- for(i=0;i<n;i++)f[i][0]=merge(0,i);
- for(i=1;i<n;i++)
- for(j=1;j<=k;j++)
- for(ak=0;ak<i;ak++)
- f[i][j]=max(f[i][j],f[ak][j-1]*merge(ak+1,i));
- cout<<f[n-1][k];
- return 0;
- }
将整数n分成k份,且每份不能为空,任意两种划分方案不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种划分方案被认为是相同的。
1 1 5
1 5 1
5 1 1
问有多少种不同的分法。
输入:n,k (6<n<=200,2<=k<=6)
输出:一个整数,即不同的分法。
7 3
4
{四种分法为:1,1,5;1,2,4;1,3,3;2,2,3;}
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- #include <cstdio>
- #include <cstdlib>
- #include <vector>
- #include <queue>
- #include <list>
- #include <deque>
- #include <string>
- using namespace std;
- const int MaxN=210;
- const int MaxM=7;
- int f[MaxN][MaxM];//f(i,j)表示数字i划分为j个数的和的方案数
- int n,m;
- int main(){
- cin>>n>>m;
- int i,j;
- f[0][0]=1;
- for(i=1;i<=n;i++)
- for(j=1;j<=m;j++)
- if(i>=j)f[i][j]+=f[i-j][j]+f[i-1][j-1];
- cout<<f[n][m];
- return 0;
- }
丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。
例如,对于下面这圈数字(n=4,m=2):
2
4 -1
3
当要求最小值时,((2-1) mod 10)×((4+3) mod 10)=1×7=7,要求最大值时,为((2+4+3) mod 10)×(-1 mod 10)=9×9=81。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。
丁丁请你编写程序帮他赢得这个游戏。
输入文件第一行有两个整数,n(1≤n≤50)和m(1≤m≤9)。以下n行每行有个整数,其绝对值不大于104,按顺序给出圈中的数字,首尾相接。
输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。
4 2
4
3
-1
2
7
81
- #include <iostream>
- #include <algorithm>
- #include <cstring>
- #include <cstdio>
- #include <cstdlib>
- #include <vector>
- #include <queue>
- #include <list>
- #include <deque>
- #include <string>
- using namespace std;
- const int MaxN=51;
- const int MaxM=10;
- const int oo=30001;
- int n,m,a[MaxN*2];//断环为链
- int fmax[MaxN*2][MaxN*2][MaxM];//f(i,j,k)表示将环中i~j划分k段对10求模后的最大乘积
- int fmin[MaxN*2][MaxN*2][MaxM];
- int p,sum[MaxN*2][MaxN*2];
- int MAX=-oo,MIN=oo;
- int main(){
- cin>>n>>m;
- int i,j,k;
- for(i=1;i<=n;i++){
- cin>>a[i];
- a[n+i]=a[i];
- }
- n<<=1;
- for(i=1;i<=n;i++)
- for(j=i;j<=n;j++){
- p=0;
- for(k=i;k<=j;k++)
- p+=a[k];
- p=p%10;
- if(p<0)p+=10;
- sum[i][j]=p;
- }
- for(i=1;i<=n;i++)
- for(j=1;j<=n;j++)
- for(k=1;k<=m;k++)
- {
- fmax[i][j][k]=-oo;
- fmin[i][j][k]=oo;
- }
- for(i=1;i<=n;i++)
- for(j=i;j<=n;j++){
- fmax[i][j][1]=sum[i][j];
- fmin[i][j][1]=sum[i][j];
- }
- for(i=1;i<=n;i++)
- for(j=i;j<=n;j++)
- for(k=2;k<=m;k++)
- for(p=j-1;p>=i;p--)
- {
- fmin[i][j][k]=min(fmin[i][j][k],fmin[i][p][k-1]*sum[p+1][j]);
- fmax[i][j][k]=max(fmax[i][j][k],fmax[i][p][k-1]*sum[p+1][j]);
- }
- n>>=1;
- for(i=1;i<=n;i++)
- {
- MAX=max(MAX,fmax[i][i+n-1][m]);
- MIN=min(MIN,fmin[i][i+n-1][m]);
- }
- cout<<MIN<<endl<<MAX;
- return 0;
- }
-