版權聲明:本文爲博主原創文章,未經博主允許不得轉載。
某國爲了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。
輸入導彈依次飛來的高度(雷達給出的高度數據是不大於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;
- }
-