1、合並石子
https://www.cnblogs.com/Renyi-Fan/p/7392649.html(講得很好)方法其實有很多種的
思路:現將石子的前綴和計算出來,狀態為 f[i][j] 表示為合並 i 到 j 的最小值。
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);

【題目描述】 在一個操場上一排地擺放着N堆石子。現要將石子有次序地合並成一堆。規定每次只能選相鄰的2堆石子合並成新的一堆,並將新的一堆石子數記為該次合並的得分。 計算出將N堆石子合並成一堆的最小得分。 【輸入】 第一行為一個正整數N (2≤N≤100); 以下N行,每行一個正整數,小於10000,分別表示第i堆石子的個數(1≤i≤N)。 【輸出】 一個正整數,即最小得分。 【輸入樣例】 7 13 7 8 16 21 4 18 【輸出樣例】 239

#include <bits/stdc++.h> using namespace std; const int N=130,inf=0x3f3f3f;; int s[N],c[N]; int f[N][N],n,x,t; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { n=read(); for(int i=1;i<=n;i++){ x=read(); s[i]=s[i-1]+x; } memset(f,127/3,sizeof(f)); for(int i=1;i<=n;i++) f[i][i]=0; for(int i=n-1;i>=1;i--) for(int j=i+1;j<=n;j++) for(int k=i;k<=j-1;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]); cout<<f[1][n]<<endl; return 0; }
合並石子,加強(圓形)求最大值和最小值
思路,形成了一個環之后,用兩倍的大小來模擬環形

Description 在一個圓形操場的四周擺放N堆石子,現要將石子有次序地合並成一堆.規定每次只能選相鄰的2堆合並成新的一堆,並將新的一堆的石子數,記為該次合並的得分。試設計出1個算法,計算出將N堆石子合並成1堆的最小得分和最大得分。 Input 輸入第一行為n(n<=100),表示有n堆石子 第二行為n個用空格隔開的整數,依次表示這n堆石子的石子數量(<=1000) Output 輸出將n堆石子合並成一堆的最小得分和將n堆石子合並成一堆的最大得分 Sample Input 3 1 2 3 Sample Output 9 11

#include <bits/stdc++.h> using namespace std; const int N=210,inf=0x3f3f3f;; int s[N],a[N]; int f[N][N],g[N][N],n,x,minn,maxn; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { n=read(); for(int i=1;i<=n;i++){ a[i]=read(); a[i+n]=a[i];//環 } for(int i=2;i<=2*n;i++) a[i]+=a[i-1]; for(int i=2*n-1;i>=1;i--) for(int j=i+1;j<=i+n-1;j++) { f[i][j]=inf; for(int k=i;k<=j-1;k++) { f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+a[j]-a[i-1]); g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]+a[j]-a[i-1]); } } minn=inf;maxn; for(int i=1;i<=n;i++){ maxn=max(maxn,g[i][i+n-1]); minn=min(minn,f[i][i+n-1]); } cout<<minn<<endl<<maxn<<endl; return 0; }
還有一種思路:%n
2、乘積最大
思路:主要是輸入的問題,將每個位置的數保存下來,用a[i][i]表示,用數組a[i][j]表示位置i到j的值是多少;

【題目描述】 今年是國際數學聯盟確定的“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≤10,1≤K≤6) 第二行是一個長度為N的數字串。 【輸出】 輸出所求得的最大乘積(一個自然數)。 【輸入樣例】 4 2 1231 【輸出樣例】 62

#include <bits/stdc++.h> using namespace std; const int N=210,inf=0x3f3f3f;; long long a[N][N],f[N][N],s; int g[N][N],n,m,minn,maxn; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { n=read();m=read(); scanf("%lld",&s); for(int i=n;i>=1;i--){ a[i][i]=s%10;//i到i位置的數 s/=10; } for(int i=2;i<=n;i++) { for(int j=i-1;j>=1;j--)//計算j到i的數字大小 a[j][i]=a[j][i-1]*10+a[i][i]; } for(int i=1;i<=n;i++) { f[i][0]=a[1][i];//沒有乘號的預處理 } for(int k=1;k<=m;k++) for(int i=k+1;i<=n;i++)//有k個乘號,則至少有k+1個數字 for(int j=k;j<i;j++)//j就是前面的 f[i][k]=max(f[i][k],f[j][k-1]*a[j+1][i]); cout<<f[n][m]<<endl; return 0; }
3、編輯距離
這個題就類似公共子序列,遇到不相同的刪除或者插入,相當於一個f[i-1][j]和f[i][j-1],如果是更改的話就是f[i-1][j-1];
if (s1[i-1]==s2[j-1]) f[i][j]=f[i-1][j-1];
else f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;

Description 設A和B是兩個字符串。我們要用最少的字符操作次數,將字符串A轉換為字符串B。這里所說的字符操作共有三種: 1、刪除一個字符; 2、插入一個字符; 3、將一個字符改為另一個字符。 對任意的兩個字符串A和B,計算出將字符串A變換為字符串B所用的最少字符操作次數。 Input 第一行為字符串A;第二行為字符串B;字符串A和B的長度均小於2000。 Output 只有一個正整數,為最少字符操作次數。 Sample Input sfdqxbw gfdgw Sample Output 4

#include <bits/stdc++.h> using namespace std; const int N=2100,inf=0x3f3f3f;; int f[N][N],n,m,minn,maxn; char s1[N],s2[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { scanf("%s %s",&s1,&s2); int n=strlen(s1),m=strlen(s2); for(int i=1;i<=n;i++) f[i][0]=i; for(int i=1;i<=m;i++) f[0][i]=i; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(s1[i-1]==s2[j-1]) f[i][j]=f[i-1][j-1]; else{ f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1; } } cout<<f[n][m]<<endl; return 0; }
4、方格取數:(有一個總結了,就不在這里寫了)
5、復制書稿
同樣也是需要求出前綴和,然后在來進行計算,輸出需要注意一下

Description 現在要把m本有順序的書分給k個人復制(抄寫),每一個人的抄寫速度都一樣,一本書不允許給兩個(或以上)的人抄寫,分給每一個人的書,必須是連續的,比如不能把第一、第三和第四本書給同一個人抄寫。 現在請你設計一種方案,使得復制時間最短。復制時間為抄寫頁數最多的人用去的時間。 Input 第一行兩個整數m,k;(k≤m≤500) 第二行m個整數,第i個整數表示第i本書的頁數。 Output 共k行,每行兩個整數,第i行表示第i個人抄寫的書的起始編號和終止編號。k行的起始編號應該從小到大排列,如果有多解,則盡可能讓前面的人少抄寫。 Sample Input 9 3 1 2 3 4 5 6 7 8 9 Sample Output 1 5 6 7 8 9

#include <bits/stdc++.h> using namespace std; const int N=505,inf=0x3f3f3f;; int a[N],s[N],f[N][N],n,m,minn,maxn,x,y,z; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } void print(int x,int ans){ if(!x) return; for(int i=x;i>=0;i--){ if(s[x]-s[i-1]>ans||!i){ print(i,ans); printf("%d %d\n",i+1,x); break; } } } int main() { n=read();m=read(); memset(f,127/3,sizeof(f)); for(int i=1;i<=n;i++) { a[i]=read(); s[i]=s[i-1]+a[i]; f[1][i]=s[i]; } for(int i=2;i<=m;i++)//人數 for(int j=1;j<=n;j++)//書 for(int k=2;k<=j;k++)//枚舉最后一個人復印開始的位置 { f[i][j]=min(f[i][j],max(f[i-1][k-1],s[j]-s[k-1])); } print(n,f[m][n]); return 0; }
6、櫥窗布置
這里初始化和輸出需要注意下,狀態表示f[i][j]:i束花放在前j個花瓶中

【題目描述】 假設以最美觀的方式布置花店的櫥窗,有FF束花,每束花的品種都不一樣,同時,至少有同樣數量的花瓶,被按順序擺成一行,花瓶的位置是固定的,並從左到右,從11到VV順序編號,VV是花瓶的數目,編號為11的花瓶在最左邊,編號為VV的花瓶在最右邊,花束可以移動,並且每束花用11到FF的整數惟一標識,標識花束的整數決定了花束在花瓶中列的順序即如果i<ji<j,則花束ii必須放在花束jj左邊的花瓶中。 例如,假設杜鵑花的標識數為11,秋海棠的標識數為22,康乃馨的標識數為33,所有的花束在放人花瓶時必須保持其標識數的順序,即:杜鵑花必須放在秋海棠左邊的花瓶中,秋海棠必須放在康乃馨左邊的花瓶中。如果花瓶的數目大於花束的數目,則多余的花瓶必須空,即每個花瓶中只能放一束花。 每一個花瓶的形狀和顏色也不相同,因此,當各個花瓶中放人不同的花束時會產生不同的美學效果,並以美學值(一個整數)來表示,空置花瓶的美學值為00。在上述例子中,花瓶與花束的不同搭配所具有的美學值,可以用如下表格表示。 根據表格,杜鵑花放在花瓶22中,會顯得非常好看,但若放在花瓶44中則顯得很難看。 為取得最佳美學效果,必須在保持花束順序的前提下,使花的擺放取得最大的美學值,如果具有最大美學值的擺放方式不止一種,則輸出任何一種方案即可。題中數據滿足下面條件:1≤F≤1001≤F≤100,F≤V≤100F≤V≤100,−50≤Aij≤50−50≤Aij≤50,其中AijAij是花束ii擺放在花瓶jj中的美學值。輸入整數FF,VV和矩陣(Aij)(Aij),輸出最大美學值和每束花擺放在各個花瓶中的花瓶編號。 花瓶1 花瓶2 花瓶3 花瓶4 花瓶5 杜鵑花 7 23 -5 -24 16 秋海棠 5 21 -4 10 23 康乃馨 -21 5 -4 -20 20 假設條件: 1≤F≤1001≤F≤100,其中 FF 為花束的數量,花束編號從 11 至 FF 。 F≤V≤100F≤V≤100,其中 VV 是花瓶的數量。 −50≤Aij≤50−50≤Aij≤50,其中 AijAij 是花束 ii 在花瓶 jj 中的美學值。 【輸入】 第一行包含兩個數:F,VF,V。 隨后的FF行中,每行包含VV個整數,AijAij 即為輸入文件中第(i+1i+1)行中的第jj個數。 【輸出】 第一行是程序所產生擺放方式的美學值。 第二行必須用FF個數表示擺放方式,即該行的第KK個數表示花束K所在的花瓶的編號。 【輸入樣例】 3 5 7 23 –5 –24 16 5 21 -4 10 23 -21 5 -4 -20 20 【輸出樣例】 53 2 4 5

#include <bits/stdc++.h> using namespace std; const int N=101,inf=0x3f3f3f3f;; int a[N][N],s[N],f[N][N],n,m,minn,maxn,x,y,z; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } void print(int x,int ans){ int i; if(x>0){ i=x; while(f[x][i]!=ans){ i++; } print(x-1,ans-a[x][i]); cout<<i<<" "; } } int main() { n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(); memset(f,128,sizeof(f)); for(int i=0;i<N;i++) f[0][i]=0; for(int i=1;i<=n;i++) for(int j=i;j<=m-n+i;j++)//保證最后剩下的瓶子夠剩下的花束用 for(int k=i;k<=j;k++) { f[i][j]=max(f[i][j],f[i-1][k-1]+a[i][k]);//i束花放在前j個花瓶中 } int c=-inf; for(int i=n;i<=m;i++) c=max(c,f[n][i]); cout<<c<<endl; print(n,c); return 0; }
7、滑雪
其實就相當於dfs的記憶化搜索

【題目描述】 小明喜歡滑雪,因為滑雪的確很刺激,可是為了獲得速度,滑的區域必須向下傾斜,當小明滑到坡底,不得不再次走上坡或等着直升機來載他,小明想知道在一個區域中最長的滑坡。滑坡的長度由滑過點的個數來計算,區域由一個二維數組給出,數組的每個數字代表點的高度。下面是一個例子: 1161514132172423123182522114192021105678912345161718196152425207142322218131211109 一個人可以從某個點滑向上下左右相鄰四個點之一,當且僅當高度減小,在上面的例子中,一條可行的滑坡為25-24-17-16-1(從25開始到1結束),當然25-24……2-1更長,事實上這是最長的一條。 【輸入】 輸入的第一行為表示區域的二維數組的行數R和列數C(1≤R、C≤100),下面是R行,每行有C個數代表高度。 【輸出】 輸出區域中最長的滑坡長度。 【輸入樣例】 5 5 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9 【輸出樣例】 25

#include <bits/stdc++.h> using namespace std; const int N=101,inf=0x3f3f3f3f;; int a[N][N],s[N],f[N][N],n,m,t; int d[4][4]={{-1,0},{0,1},{1,0},{0,-1}}; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int dfs(int x,int y) { if(f[x][y]) return f[x][y]; int num=1; for(int i=0;i<4;i++) { int nx=x+d[i][0],ny=y+d[i][1]; if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&a[x][y]<a[nx][ny]) { int temp=dfs(nx,ny)+1; num=max(num,temp); } } f[x][y]=num; return num; } int main() { n=read();m=read(); int ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { a[i][j]=read(); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { t=dfs(i,j); f[i][j]=t; ans=max(ans,t); } cout<<ans<<endl; return 0; }
8、公共子序列(就是最大上升子序列的多組數據版)

【題目描述】 我們稱序列Z=<z1,z2,...,zk>Z=<z1,z2,...,zk>是序列X=<x1,x2,...,xm>X=<x1,x2,...,xm>的子序列當且僅當存在嚴格上升的序列<i1,i2,...,ik><i1,i2,...,ik>,使得對j=1,2,...,k,有xij=zjxij=zj。比如Z=<a,b,f,c> 是X=<a,b,c,f,b,c>的子序列。 現在給出兩個序列X和Y,你的任務是找到X和Y的最大公共子序列,也就是說要找到一個最長的序列Z,使得Z既是X的子序列也是Y的子序列。 【輸入】 輸入包括多組測試數據。每組數據包括一行,給出兩個長度不超過200的字符串,表示兩個序列。兩個字符串之間由若干個空格隔開。 【輸出】 對每組輸入數據,輸出一行,給出兩個序列的最大公共子序列的長度。 【輸入樣例】 abcfbc abfcab programming contest abcd mnp 【輸出樣例】 4 2 0

#include <bits/stdc++.h> using namespace std; const int N=501,inf=0x3f3f3f3f;; int f[N][N],n,m,t; char a[N],b[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { while(scanf("%s %s",a+1,b+1)!=EOF) { int len1=strlen(a+1),len2=strlen(b+1); for(int i=1;i<=len1;i++) f[i][0]=0; for(int j=1;j<=len2;j++) f[0][j]=0; for(int i=1;i<=len1;i++) for(int j=1;j<=len2;j++) { if(a[i]==b[j]) f[i][j]=f[i-1][j-1]+1; else f[i][j]=max(f[i-1][j],f[i][j-1]); } cout<<f[len1][len2]<<endl; } return 0; }
9、糖果
比較巧妙的思想就是將狀態表示為除以k的余數;f[i][j]表示i件產品的糖果個數除以k的余數的最大總數

Description 由於在維護世界和平的事務中做出巨大貢獻,Dzx被贈予糖果公司2010年5月23日當天無限量糖果免費優惠券。在這一天,Dzx可以從糖果公司的N件產品中任意選擇若干件帶回家享用。糖果公司的N件產品每件都包含數量不同的糖果。Dzx希望他選擇的產品包含的糖果總數是K的整數倍,這樣他才能平均地將糖果分給幫助他維護世界和平的伙伴們。當然,在滿足這一條件的基礎上,糖果總數越多越好。Dzx最多能帶走多少糖果呢? 注意:Dzx只能將糖果公司的產品整件帶走。 Input 第一行包含兩個整數N( 1<=N<=100 )和K( 1<=K<=100 )。 以下N行每行1個整數,表示糖果公司該件產品中包含的糖果數目,不超過1000000。 Output 符合要求的最多能達到的糖果總數,如果不能達到K的倍數這一要求,輸出0。 Sample Input 5 7 1 2 3 4 5 Sample Output 14

#include <bits/stdc++.h> using namespace std; const int N=1005,inf=0x3f3f3f3f;; int f[N][N],n,m; int a[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<m;i++) f[0][i]=-inf; for(int i=1;i<=n;i++) for(int j=0;j<m;j++) f[i][j]=max(f[i-1][j],f[i-1][(m+j-a[i]%m)%m]+a[i]);// cout<<f[n][0]<<endl; return 0; }
10、雞蛋的硬度
對於這個題:當時寫的是我感覺自己不是很理解這個最優策略為啥這樣做
最優策略指在最壞情況下所需要的扔雞蛋次數最少的策略。
代碼就先放在這里

Description 最近XX公司舉辦了一個奇怪的比賽:雞蛋硬度之王爭霸賽。參賽者是來自世界各地的母雞,比賽的內容是看誰下的蛋最硬,更奇怪的是XX公司並不使用什么精密儀器來測量蛋的硬度,他們采用了一種最老土的辦法--從高度扔雞蛋--來測試雞蛋的硬度,如果一次母雞下的蛋從高樓的第a層摔下來沒摔破,但是從a+1層摔下來時摔破了,那么就說這只母雞的雞蛋的硬度是a。你當然可以找出各種理由說明這種方法不科學,比如同一只母雞下的蛋硬度可能不一樣等等,但是這不影響XX公司的爭霸賽,因為他們只是為了吸引大家的眼球,一個個雞蛋從100 層的高樓上掉下來的時候,這情景還是能吸引很多人駐足觀看的,當然,XX公司也絕不會忘記在高樓上掛一條幅,寫上“XX公司”的字樣--這比賽不過是XX 公司的一個另類廣告而已。 勤於思考的小A總是能從一件事情中發現一個數學問題,這件事也不例外。“假如有很多同樣硬度的雞蛋,那么我可以用二分的辦法用最少的次數測出雞蛋的硬度”,小A對自己的這個結論感到很滿意,不過很快麻煩來了,“但是,假如我的雞蛋不夠用呢,比如我只有1個雞蛋,那么我就不得不從第1層樓開始一層一層的扔,最壞情況下我要扔100次。如果有2個雞蛋,那么就從2層樓開始的地方扔……等等,不對,好像應該從1/3的地方開始扔才對,嗯,好像也不一定啊……3個雞蛋怎么辦,4個,5個,更多呢……”,和往常一樣,小A又陷入了一個思維僵局,與其說他是勤於思考,不如說他是喜歡自找麻煩。 好吧,既然麻煩來了,就得有人去解決,小A的麻煩就靠你來解決了:) Input 輸入包括多組數據,每組數據一行,包含兩個正整數n和m( 1<=n<=100,1<=m<=10 ),其中n表示樓的高度,m表示你現在擁有的雞蛋個數,這些雞蛋硬度相同(即它們從同樣高的地方掉下來要么都摔碎要么都不碎),並且小於等於n。你可以假定硬度為x的雞蛋從高度小於等於x的地方摔無論如何都不會碎(沒摔碎的雞蛋可以繼續使用),而只要從比x高的地方扔必然會碎。 對每組輸入數據,你可以假定雞蛋的硬度在0至n之間,即在n+1層扔雞蛋一定會碎。 Output 對於每一組輸入,輸出一個整數,表示使用最優策略在最壞情況下所需要的扔雞蛋次數。 Sample Input 100 1 100 2 Sample Output 100 14

#include <bits/stdc++.h> using namespace std; const int N=1005,inf=0x3f3f3f3f;; int f[N][N];//第i樓和扔j次蛋 int n,m; int a[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { while(scanf("%d %d",&n,&m)!=EOF){ for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) f[i][j]=i; for(int i=1;i<=n;i++) for(int k=1;k<=i;k++)//中間變量,在這個地方進行判斷轉折與否(也就是碎了往下走,沒碎往上走) for(int j=2;j<=m;j++) f[i][j]=min(f[i][j],max(f[k-1][j-1],f[i-k][j])+1); cout<<f[n][m]<<endl; } return 0; }
11、大盜阿福
思路不難,就是要么不取,就等於f[i-1],要么偷這一個上一個就不能偷,就是f[i-2]+a[i];
f[i]=max(f[i-1],f[i-2]+a[i]);

【題目描述】 阿福是一名經驗豐富的大盜。趁着月黑風高,阿福打算今晚洗劫一條街上的店鋪。 這條街上一共有 NN 家店鋪,每家店中都有一些現金。阿福事先調查得知,只有當他同時洗劫了兩家相鄰的店鋪時,街上的報警系統才會啟動,然后警察就會蜂擁而至。 作為一向謹慎作案的大盜,阿福不願意冒着被警察追捕的風險行竊。他想知道,在不驚動警察的情況下,他今晚最多可以得到多少現金? 【輸入】 輸入的第一行是一個整數T(T≤50)T(T≤50) ,表示一共有T組數據。 接下來的每組數據,第一行是一個整數N(1≤N≤100,000)N(1≤N≤100,000) ,表示一共有NN家店鋪。第二行是NN個被空格分開的正整數,表示每一家店鋪中的現金數量。每家店鋪中的現金數量均不超過10001000。 【輸出】 對於每組數據,輸出一行。該行包含一個整數,表示阿福在不驚動警察的情況下可以得到的現金數量。 【輸入樣例】 2 3 1 8 2 4 10 7 6 14 【輸出樣例】 8 24 【提示】 對於第一組樣例,阿福選擇第22家店鋪行竊,獲得的現金數量為88。 對於第二組樣例,阿福選擇第11和44家店鋪行竊,獲得的現金數量為10+14=2410+14=24。

#include <bits/stdc++.h> using namespace std; const int N=100005,inf=0x3f3f3f3f;; int f[N];//第i樓和扔j次蛋 int n,m; int a[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { n=read(); while(n--){ m=read(); for(int i=1;i<=m;i++) a[i]=read(); f[0]=0;f[1]=a[1]; for(int i=2;i<=m;i++) f[i]=max(f[i-1],f[i-2]+a[i]); cout<<f[m]<<endl; } return 0; }
12、股票買賣
因為是買賣兩次,兩邊循環兩次

【題目描述】 最近越來越多的人都投身股市,阿福也有點心動了。謹記着“股市有風險,入市需謹慎”,阿福決定先來研究一下簡化版的股票買賣問題。 假設阿福已經准確預測出了某只股票在未來N天的價格,他希望買賣兩次,使得獲得的利潤最高。為了計算簡單起見,利潤的計算方式為賣出的價格減去買入的價格。 同一天可以進行多次買賣。但是在第一次買入之后,必須要先賣出,然后才可以第二次買入。 現在,阿福想知道他最多可以獲得多少利潤。 【輸入】 輸入的第一行是一個整數T(T≤50),表示一共有T組數據。 接下來的每組數據,第一行是一個整數N(1≤N≤100,000),表示一共有N天。第二行是 N 個被空格分開的整數,表示每天該股票的價格。該股票每天的價格的絕對值均不會超過1,000,000。 【輸出】 對於每組數據,輸出一行。該行包含一個整數,表示阿福能夠獲得的最大的利潤。 【輸入樣例】 3 7 5 14 -2 4 9 3 17 6 6 8 7 4 1 -2 4 18 9 5 2 【輸出樣例】 28 2 0 【提示】 對於第一組樣例,阿福可以第1次在第1天買入(價格為5),然后在第2天賣出(價格為14)。第2次在第3天買入(價格為-2),然后在第7天賣出(價格為17)。一共獲得的利潤是(14-5)+(17-(-2))=28。 對於第二組樣例,阿福可以第1次在第1天買入(價格為6),然后在第2天賣出(價格為8)。第2次仍然在第2天買入,然后在第2天賣出。一共獲得的利潤是8-6=2。 對於第三組樣例,由於價格一直在下跌,阿福可以隨便選擇一天買入之后迅速賣出。獲得的最大利潤為0。 經典算法Baidu搜索,深刻體會。

#include <bits/stdc++.h> using namespace std; const int N=100005,inf=0x3f3f3f3f;; int f[N],g[N];//f為i天前的最大利潤 int n,m,minn,maxx; int a[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { n=read(); while(n--){ m=read(); memset(a,0,sizeof(a)); memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); for(int i=1;i<=m;i++) a[i]=read(); f[1]=0;g[1+n]=0; minn=inf;maxx=-inf; for(int i=1;i<=m;i++){ minn=min(minn,a[i]); f[i]=max(f[i-1],a[i]-minn);//i天前的最大 } for(int i=m;i>=1;i--) { maxx=max(maxx,a[i]); g[i]=max(g[n-1],maxx-a[i]); } int ans=-inf; for(int i=1;i<=m;i++) ans=max(ans,f[i]+g[i]); cout<<ans<<endl; } return 0; }
13、鳴人的影分身
我自己的做法其實就是分蘋果那種做法,用動態規划的話,參考了一下別人的代碼

【題目描述】 在火影忍者的世界里,令敵人捉摸不透是非常關鍵的。我們的主角漩渦鳴人所擁有的一個招數——多重影分身之術——就是一個很好的例子。 影分身是由鳴人身體的查克拉能量制造的,使用的查克拉越多,制造出的影分身越強。 針對不同的作戰情況,鳴人可以選擇制造出各種強度的影分身,有的用來佯攻,有的用來發起致命一擊。 那么問題來了,假設鳴人的查克拉能量為M,他影分身的個數最多為N,那么制造影分身時有多少種(用K表示)不同的分配方法?(影分身可以被分配到0點查克拉能量) 【輸入】 第一行是測試數據的數目t(0≤t≤20)。以下每行均包含二個整數M和N(1≤M,N≤10),以空格分開。 【輸出】 對輸入的每組數據M和N,用一行輸出相應的K。 【輸入樣例】 1 7 3 【輸出樣例】 8

int t,n,m; int f[11][11]; int main(){ cin>>t; for(int i=0;i<=10;i++){//能量 for(int j=0;j<=10;j++){//身體數 if(j==1||i==0||i==1) f[i][j]=1; //一個身體、0點或1點能量-----都只有一種方案 else if(j>i) f[i][j]=f[i][i]; else f[i][j]=f[i-j][j]+f[i][j-1]; //第一種情況:每個分身都有1點能量加上多余的能產生的 //第二種情況:減少一個身體能夠產生的 //分為無0和有0 !!!!!記住 } } while(t--){ cin>>n>>m; cout<<f[n][m]<<endl; } return 0; }

#include <bits/stdc++.h> using namespace std; const int N=100005,inf=0x3f3f3f3f;; int g[N];//f為i天前的最大利潤 int t; int a[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int f(int m,int n) { if(m==0||n==1) return 1; if(m<n) return f(m,m); else return f(m,n-1)+f(m-n,n); } int main() { int n,m; t=read(); while(t--){ m=read();n=read(); cout<<f(m,n)<<endl; } return 0; }
14、數的划分

Description 將整數n分成k份,且每份不能為空,任意兩份不能相同(不考慮順序)。 例如:n=7,k=3,下面三種分法被認為是相同的。 1,1,5; 1,5,1; 5,1,1; 問有多少種不同的分法。 輸出一個整數,即不同的分法。 Input 兩個整數n,k( 6 < n <= 200,2 <= k <= 6),中間用單個空格隔開。 Output 一個整數,即不同的分法。 Sample Input 7 3 Sample Output 4 Hint 四種分法為:1,1,5;1,2,4;1,3,3;2,2,3。

//yrnddup c++ code #include <bits/stdc++.h> using namespace std; const int N=1005; int f[N][N]; int t; int a[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { int n,m; n=read(); m=read(); f[0][0]=1; for(int i=1;i<=m;i++) for(int j=i;j<=n;j++) f[i][j]=f[i][j-i]+f[i-1][j-1]; cout<<f[m][n]<<endl; return 0; }
15、Maximum sum

【題目描述】 對於給定的整數序列A={a1,a2,...,an}A={a1,a2,...,an},找出兩個不重合連續子段,使得兩子段中所有數字的和最大。我們如下定義函數 d(A)d(A): d(A)=max1≤s1≤t1≤s2≤t2≤n{∑i=s1t1ai+∑j=s2t2aj} d(A)=max1≤s1≤t1≤s2≤t2≤n{∑i=s1t1ai+∑j=s2t2aj} 我們的目標就是求出d(A)d(A)。 【輸入】 第一行是一個整數T(≤30)T(≤30),代表一共有多少組數據。 接下來是TT組數據。 每組數據的第一行是一個整數,代表數據個數據n(2≤n≤50000)n(2≤n≤50000) ,第二行是nn個整數a1,a2,...,an(|ai|≤10000)a1,a2,...,an(|ai|≤10000)。 【輸出】 輸出一個整數,就是d(A)d(A)的值。 【輸入樣例】 1 10 1 -1 2 2 3 -3 4 -4 5 -5 【輸出樣例】 13 【提示】 就是求最大子段和問題,樣列取2,2,3,−3,42,2,3,−3,4和55,Baidu搜POJ 2479 Maximum sum,可獲得大量經典最大子段和問題的題目解析,本題O(n2)O(n2)算法超時,必須用O(n)O(n)算法。

#include <bits/stdc++.h> using namespace std; const int N=50005; int rf[N],lf[N],lmax[N],rmax[N],ans; int t,n; int a[N]; inline int read() { int x=0;char y='*',z=getchar(); while(z<'0'||z>'9') y=z,z=getchar(); while(z>='0'&&z<='9') x=(x<<3)+(x<<1)+(z-'0'),z=getchar(); return y=='-'?-x:x; } int main() { t=read(); while(t--) { n=read(); for(int i=1;i<=n;i++) a[i]=read(); lf[1]=a[1]; rf[n]=a[n]; lmax[1]=a[1]; rmax[n]=a[n]; for(int i=2;i<=n;i++) lf[i]=max(a[i],lf[i-1]+a[i]);//從左往右的到i的最大值 for(int i=n-1;i>=1;i--) rf[i]=max(a[i],rf[i+1]+a[i]);//從右往左到i的最大值; for(int i=2;i<=n;i++) lmax[i]=max(lmax[i-1],lf[i]);//選擇某一個區間的最大值 for(int i=n-1;i>=1;i--) rmax[i]=max(rmax[i+1],rf[i]);//選擇某一個區間的最大值 int ans=a[1]; for(int i=2;i<=n;i++) ans=max(ans,lmax[i-1]+rmax[i]); cout<<ans<<endl; } return 0; }
16、最長上升公共子序列
經典,結合在一起的,專題專題,但是我這種做法在另一個網站不過

【題目描述】 給定兩個整數序列,寫一個程序求它們的最長上升公共子序列。 當以下條件滿足的時候,我們將長度NN的序列S1,S2,...,SNS1,S2,...,SN 稱為長度為MM的序列A1,A2,...,AMA1,A2,...,AM的上升子序列: 存在1≤i1<i2<...<iN≤M1≤i1<i2<...<iN≤M,使得對所有1≤j≤N1≤j≤N,均有Sj=AijSj=Aij,且對於所有的1≤j<N1≤j<N,均有Sj<Sj+1Sj<Sj+1。 【輸入】 每個序列用兩行表示,第一行是長度M(1≤M≤500)M(1≤M≤500),第二行是該序列的M個整數Ai(−231≤Ai<231)Ai(−231≤Ai<231) 【輸出】 在第一行,輸出兩個序列的最長上升公共子序列的長度LL。在第二行,輸出該子序列。如果有不止一個符合條件的子序列,則輸出任何一個即可。 【輸入樣例】 5 1 4 2 5 -12 4 -12 1 2 4 【輸出樣例】 2 1 4 【提示】 經典算法Baidu搜索,深刻體會。

#include <bits/stdc++.h> using namespace std; const int N=505; long long a[N],b[N]; int f[N][N],pre[N][N],res[N]; int n,m; int main() { cin>>n; for(int i=1;i<=n;i++) scanf("%d",&a[i]); cin>>m; for(int i=1;i<=m;i++) scanf("%d",&b[i]); int ans=0,loci,locj; for(int i=1;i<=n;i++) { int maxx=0,loc=0; for(int j=1;j<=m;j++) { f[i][j]=f[i-1][j]; pre[i][j]=j; if(a[i]>b[j]&&maxx<f[i-1][j]) { maxx=f[i-1][j]; loc=j; } else if(a[i]==b[j]) { f[i][j]=maxx+1; pre[i][j]=loc; } if(f[i][j]>ans) { ans=f[i][j]; loci=i; locj=j; } } } cout<<ans<<endl; int num=0; while(f[loci][locj]) { while(a[loci]!=b[locj]&&loci) loci--; res[++num]=b[locj]; locj=pre[loci][locj]; } for(int i=num;i>=1;i--) cout<<res[i]<<" "; cout<<endl; return 0; }