2016.4.2 动态规划练习--讲课整理
1.codevs1742 爬樓梯
?時(shí)間限制: 1 s ?空間限制: 128000 KB ?題目等級(jí) : 黃金 Gold 題目描述?Description小明家外面有一個(gè)長長的樓梯,共N階。小明的腿很長,一次能跨過一或兩階。有一天,他突發(fā)奇想,想求出從最低階到最高階共有幾種爬樓梯的方案。你幫幫他吧!
輸入描述?Input Description一個(gè)整數(shù)N。
輸出描述?Output Description一個(gè)整數(shù),為方案總數(shù)。
樣例輸入?Sample Input5
樣例輸出?Sample Output8
數(shù)據(jù)范圍及提示?Data Size & Hint0≤N≤40
#include<iostream> #include<cstdio> using namespace std; long long int a[41]; int n; int main() {scanf("%d",&n);if(n==0){cout<<0;return 0;}a[1]=1;a[2]=2;for(int i=3;i<=n;++i)a[i]=a[i-1]+a[i-2];cout<<a[n]<<endl;return 0; }一般代碼
#include<iostream> int n; #include<cstring> using namespace std; #include<cstdio> const int INF=10001; int a[INF],b[INF],c[INF]; int lena=1,lenb=1,lenc=0; void count() {lenc=1;int x=0;while(lenc<=lenb||lenc<=lenb){c[lenc]=a[lenc]+b[lenc]+x;x=c[lenc]/10;c[lenc]%=10;lenc++;}c[lenc]+=x;if(c[lenc]==0)lenc--;return ; } void SWAP() {memset(a,0,sizeof(a));//strcpy(a,b);for(int i=1;i<=lenb;++i)a[i]=b[i];lena=lenb;memset(b,0,sizeof(b));for(int i=1;i<=lenc;++i)b[i]=c[i]; // strcpy(b,c);lenb=lenc;memset(c,0,sizeof(c));lenc=0; } int main() {scanf("%d",&n);a[1]=1;b[1]=2;for(int i=3;i<=n;++i){count();SWAP();}for(int i=lenb;i>=1;--i)printf("%d",b[i]);return 0; }高精度代碼
?2.codevs1259 最大正方形子矩陣
?時(shí)間限制: 1 s ?空間限制: 128000 KB ?題目等級(jí) : 黃金 Gold? 題目描述?Description在一個(gè)01矩陣中,包含有很多的正方形子矩陣,現(xiàn)在要求出這個(gè)01矩陣中,最大的正方形子矩陣,使得這個(gè)正方形子矩陣中的某一條對(duì)角線上的值全是1,其余的全是0。
輸入描述?Input Description第一行有兩個(gè)整數(shù)n和m(1<=n,m<=1000)。接下來的n行,每行有m個(gè)0或1的數(shù)字。每兩個(gè)數(shù)字之間用空格隔開。
?
輸出描述?Output Description只有一個(gè)整數(shù),即這個(gè)滿足條件的最大的正方形子矩陣的邊長。
樣例輸入?Sample Input4?6
0?1?0?1?0?0
0?0?1?0?1?0
1?1?0?0?0?1
0?1?1?0?1?0
樣例輸出?Sample Output3
/*基本思路:統(tǒng)計(jì)每個(gè)點(diǎn)左上右各有多少個(gè)0(除自身以外),找最大正 方形子矩陣的時(shí)候,就以值為1的點(diǎn),判斷 他的左上(右上),上,左(右)各有多少個(gè)0,取一個(gè)小數(shù)后 加1,就是以當(dāng)前這個(gè)點(diǎn)為左下角或者右下角的正方形的最大邊長。 想法;因?yàn)轭}目中的1對(duì)角線是最難處理的,所以就把這個(gè)1作為突破口*/ #include<iostream> using namespace std; #include<cstdio> #define N 1001 int n,m; struct Poi{int l,r,num,ans,up; }; Poi poi[N][N]; int maxx=-N; void update() {for(int i=2;i<=n;++i)/*分別統(tǒng)計(jì)左上右各有多少個(gè)0*/for(int j=1;j<=m;++j){if(poi[i-1][j].num==0)poi[i][j].up=poi[i-1][j].up+1;}for(int j=2;j<=m;++j)for(int i=1;i<=n;++i){if(poi[i][j-1].num==0)poi[i][j].l=poi[i][j-1].l+1;}for(int j=m-1;j>=1;--j)for(int i=1;i<=n;++i)/*注意不同的尋找for循環(huán)的順序是不同的*/{if(poi[i][j+1].num==0)poi[i][j].r=poi[i][j+1].r+1;} } void input() {scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){scanf("%d",&poi[i][j].num);}update(); } void countz()/*求左上方的正方形的最大邊長*/ {for(int i=1;i<=m;++i)if(poi[1][i].num==1)poi[1][i].ans=1;for(int i=1;i<=n;++i)if(poi[i][1].num==1)poi[i][1].ans=1;for(int i=2;i<=n;++i)/*注意不同的尋找for循環(huán)的順序是不同的*/for(int j=2;j<=m;++j){if(poi[i][j].num==1)poi[i][j].ans=min(min(poi[i][j].l,poi[i][j].up),poi[i-1][j-1].ans)+1;if(poi[i][j].ans>maxx)maxx=poi[i][j].ans;} } void county() {for(int i=1;i<=n;++i)poi[i][m].ans=1;for(int i=2;i<=n;++i)for(int j=m-1;j>=1;--j){if(poi[i][j].num==1)poi[i][j].ans=min(min(poi[i][j].r,poi[i][j].up),poi[i-1][j+1].ans)+1;if(poi[i][j].ans>maxx)maxx=poi[i][j].ans;} } int main() {input();countz();county();printf("%d\n",maxx);return 0; }View Code
?3. noi?1759:最長上升子序列(nlogn算法)
- 總時(shí)間限制:?
- 2000ms 內(nèi)存限制:?
- 65536kB
- 描述
- 一個(gè)數(shù)的序列bi,當(dāng)b1?<?b2?< ... <?bS的時(shí)候,我們稱這個(gè)序列是上升的。對(duì)于給定的一個(gè)序列(a1,?a2, ...,?aN),我們可以得到一些上升的子序列(ai1,?ai2, ...,?aiK),這里1 <=?i1?<?i2?< ... <?iK?<= N。比如,對(duì)于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。這些子序列中最長的長度是4,比如子序列(1, 3, 5, 8).
你的任務(wù),就是對(duì)于給定的序列,求出最長上升子序列的長度。 輸入 - 輸入的第一行是序列的長度N (1 <= N <= 1000)。第二行給出序列中的N個(gè)整數(shù),這些整數(shù)的取值范圍都在0到10000。 輸出
- 最長上升子序列的長度。 樣例輸入
-
7 1 7 3 5 9 4 8
樣例輸出4
#include<iostream> using namespace std; #include<cstdio> const int INF=10001; #include<cstring> const int N=1001; long long a[N],c[N],f[N]; int search(int l,int r,int i)/*二分查找*/ {if(l==r) return l;int mid=(l+r+1)/2;if(c[mid]>=a[i]) return search(l,mid-1,i);/*等號(hào)加到上面是上升序列*/if(c[mid]<a[i]) return search(mid,r,i);/*等號(hào)加到下面是不下降*/ } int main() {int n;scanf("%d",&n);for(int i=1;i<=n;++i)scanf("%d",&a[i]);memset(c,127,sizeof(c));long long int MAX=-INF;for(int i=1;i<=n;++i){f[i]=search(0,i,i)+1;c[f[i]]=min(c[f[i]],a[i]);MAX=max(f[i],MAX);}printf("%d\n",MAX);return 0; }View Code
?
- 4.1166 矩陣取數(shù)游戲2007年NOIP全國聯(lián)賽提高組時(shí)間限制: 1 s
【問題描述】
帥帥經(jīng)常跟同學(xué)玩一個(gè)矩陣取數(shù)游戲:對(duì)于一個(gè)給定的n*m 的矩陣,矩陣中的每個(gè)元素aij均
為非負(fù)整數(shù)。游戲規(guī)則如下:
1. 每次取數(shù)時(shí)須從每行各取走一個(gè)元素,共n個(gè)。m次后取完矩陣所有元素;
2. 每次取走的各個(gè)元素只能是該元素所在行的行首或行尾;
3. 每次取數(shù)都有一個(gè)得分值,為每行取數(shù)的得分之和,每行取數(shù)的得分= 被取走的元素值*2i,
其中i 表示第i 次取數(shù)(從1 開始編號(hào));
4. 游戲結(jié)束總得分為m次取數(shù)得分之和。
帥帥想請(qǐng)你幫忙寫一個(gè)程序,對(duì)于任意矩陣,可以求出取數(shù)后的最大得分。
第1行為兩個(gè)用空格隔開的整數(shù)n和m。
第2~n+1 行為n*m矩陣,其中每行有m個(gè)用單個(gè)空格隔開的非負(fù)整數(shù)。
輸出 僅包含1 行,為一個(gè)整數(shù),即輸入矩陣取數(shù)后的最大得分。
樣例輸入?Sample Input2 3
1 2 3
3 4 2
82
數(shù)據(jù)范圍及提示?Data Size & Hint樣例解釋
第 1 次:第1 行取行首元素,第2 行取行尾元素,本次得分為1*21+2*21=6
第2 次:兩行均取行首元素,本次得分為2*22+3*22=20
第3 次:得分為3*23+4*23=56。總得分為6+20+56=82
【限制】
60%的數(shù)據(jù)滿足:1<=n, m<=30, 答案不超過1016
100%的數(shù)據(jù)滿足:1<=n, m<=80, 0<=aij<=1000
?代碼:
/*分析:對(duì)于區(qū)間型DP,f[i][j],一般表示的不是i--j這個(gè)區(qū)間,而是從該開始延伸j位,這樣在for循環(huán)中可以方便轉(zhuǎn)移。 由題意,行與行之間沒有關(guān)系,可以單獨(dú)處理一行的問題。 考慮一行數(shù)據(jù), 在區(qū)間[i..j]中取數(shù)可以轉(zhuǎn)化為以下兩種情況: 1.先取i,再在[i+1..j]中取; 2.先取j,再在[i..j-1]中取。 f(i,1)=2*a[i] f(i,j)=Max{2*f(i+1,j-1)+f(i,1),2*f[i,j-1]+f(i+j-1,1) 注意這里為什么沒有題目中要求的2^x呢?因?yàn)楫?dāng)你把f[i+1][j-1]一層層推進(jìn)去的時(shí)候,你會(huì)發(fā)現(xiàn),其實(shí)在這個(gè)區(qū)間內(nèi)部的數(shù),會(huì)被乘了多次2,這就是題目中要求的后一個(gè)取的數(shù)比前一項(xiàng)多乘一個(gè)2. n<=80,須涉及到高精度運(yùn)算。 位數(shù)估算:2^80*a[i]~2^90,十進(jìn)制下大約27位。 注意數(shù)組f的清零初始化,否則會(huì)導(dǎo)致位數(shù)錯(cuò)誤。 */ #include<iostream> using namespace std; #include<cstdio> #define N 1001 #define M 81 int a[M],n,m; int f[M][M][N],ans[N]; #include<cstring> void add(int *s,int *t)//s+=t {int len=max(s[0],t[0]);int i=1;while(i<=len){s[i]+=t[i];s[i+1]+=s[i]/10;s[i]%=10;i++;}if(s[len+1]) len++;s[0]=len; } int cmp(int *s,int *t)//t1>t2 fan hui zheng shu {if(s[0]>t[0]) return 1;if(t[0]>s[0]) return -1;for(int i=s[0];i>0;--i){if(s[i]>t[i]) return 1;if(t[i]>s[i]) return -1;}return 1; } void cpy(int *s,int* t )//ba t jia ren s {memset(s,0,sizeof(s));s[0]=t[0];for(int i=1;i<=t[0];++i)s[i]=t[i]; } int main() {scanf("%d%d",&n,&m);int t1[N],t2[N];for(int i=1;i<=n;++i)/*每輸入一行就處理一行*/{memset(a,0,sizeof(a));memset(f,0,sizeof(f));for(int j=1;j<=m;++j){scanf("%d",&a[j]);//a[j]*=2;int len=1;for(len=1;a[j];++len){f[j][1][len]=a[j]%10;a[j]/=10;}/*注意點(diǎn)一:這里不能用add(f[j][1],f[j][1]),因?yàn)閭魅胱雍瘮?shù)中的是s,t雖然是相加,但是因?yàn)橹羔樦傅牡氖峭粋€(gè)地址,那么加的過程中,不僅s在變化,t也在變化,那就是不是我們想要的加法了;*/f[j][1][0]=len;cpy(t1,f[j][1]);add(f[j][1],t1);memset(t1,0,sizeof(t1));}for(int l=2;l<=m;++l){for(int j=1;j+l-1<=m;++j){memset(t1,0,sizeof(t1));memset(t2,0,sizeof(t2));add(t1,f[j+1][l-1]);add(t1,f[j+1][l-1]);add(t1,f[j][1]);/*動(dòng)態(tài)規(guī)劃方程不一定有相應(yīng)的簡短的形式*/add(t2,f[j][l-1]);add(t2,f[j][l-1]);add(t2,f[j+l-1][1]);if(cmp(t1,t2)>0)/*strcmp,和strcpy只適用于字符串,而不是用于int數(shù)組*/cpy(f[j][l],t1);else cpy(f[j][l],t2);}}add(ans,f[1][m]);}for(int i=ans[0];i>0;--i)printf("%d",ans[i]);printf("\n");return 0; }View Code
?
轉(zhuǎn)載于:https://www.cnblogs.com/c1299401227/p/5346855.html
總結(jié)
以上是生活随笔為你收集整理的2016.4.2 动态规划练习--讲课整理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么将jenkins打包后的war自动部
- 下一篇: android pop3与imap方式接