生活随笔
收集整理的這篇文章主要介紹了
区间动态规划
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1、矩陣連乘,tyvj 1198(最優(yōu)矩陣連乘),關(guān)鍵是寫出動(dòng)態(tài)轉(zhuǎn)移方程。用DP[i][j]表示矩陣Ai乘到Aj的最優(yōu)解,P[]用來存儲(chǔ)矩陣的行和列,M[i-1]表示矩陣i的行,M[i]表示矩陣i的列。
當(dāng)i==j時(shí),DP[i][i]=0;
當(dāng)i<j時(shí),可以假設(shè)他從k位置隔開。比方說從A3..A8。那么A1A2(A3A4A5A6A7A8)A9。我們知道若A矩陣為r1*c的矩陣,A2為c*r2的矩陣,得到的將是一個(gè)r1*r2的矩陣,且連乘次數(shù)為r1*c*r2。假設(shè)我從5位置隔開,就可以分為A1A2((A3A4A5)(A6A7A8))A9。那么這個(gè)時(shí)候的次數(shù)為:DP[3][5]+DP[6][8]+M[2]*M[5]*M[8](A3的行*A5的列*A8的列)。那么我只需要模舉這其中的組合然后取最小值即可,這個(gè)時(shí)候的動(dòng)態(tài)轉(zhuǎn)移方程為:DP[i][j]=min(DP[i][k]+DP[k+1][j]+M[i-1]*M[k]*M[j])(i<=k<j)。
問題是:你要知道DP[i][k]和DP[k+1][j]的值,你才可以和DP[i][j]做比較,才能求出DP[i][j]的值。
思想是先求出相鄰組的連乘次數(shù),如一開始算相鄰兩個(gè)相乘,如圖:
那么每?jī)蓚€(gè)相乘的結(jié)果我們可以得到。同理繼續(xù)求三個(gè)相鄰的結(jié)果。
計(jì)算的話比方說要求A1A2A3,可以化為(A1)(A2A3)=DP[1][1]+DP[2][3]+M[0]*M[1]*M[3],依次類推結(jié)果為:DP[i][j]=DP[i][i]+DP[i+1][j]+M[i-1]*M[i]*M[j]。
只有這樣做后我才可以求任意組合的次數(shù),求出三個(gè)一組的結(jié)果后,我就可以求任意的四個(gè)矩陣連乘的最小值。如:A4A5A6A7,隨便怎么組合,通過上面是計(jì)算都可以求出相應(yīng)的結(jié)果。
現(xiàn)在討論下范圍:用r保存要求的每每相鄰的組數(shù)(2<=r<=n)(從2開始是這里前提條件是i<j),i的范圍為:(1<=i<=n-r+1,稍作推理就知道,當(dāng)r=3時(shí),即三個(gè)三個(gè)一組時(shí)i<=9-3+1=7),j的值固定j=r+i-1,具體見代碼:
[cpp]?view plaincopyprint?
#include<iostream>?? #include<cstring>?? #include<cstdio>?? using?namespace?std;?? const?int?MAX=110;?? #define?min(a,b)?(a)<(b)?(a):(b)?? #define?CLR(arr,val)?memset(arr,val,sizeof(arr))?? int?n,M[MAX],DP[MAX][MAX];?? void?Matrix()?? {???for(int?r=2;r<=n;r++)?????????? ????????for(int?i=1;i<=n-r+1;i++)???? ????????{???int?j=r+i-1;?? ????????????DP[i][j]=DP[i+1][j]+M[i-1]*M[i]*M[j];?? ????????????for(int?k=i;k<j;k++)?? ????????????????DP[i][j]=min(DP[i][k]+DP[k+1][j]+M[i-1]*M[k]*M[j],DP[i][j]);?? ????????}??? }?? int?main()?? {???scanf("%d",&n);?? ????for(int?i=0;i<=n;i++)?? ????????scanf("%d",&M[i]);??? ????CLR(DP,0);?? ????Matrix();?? ????cout<<DP[1][n]<<endl;??????? ????return?0;?? }??
路徑輸出:
[cpp]?view plaincopyprint?
#include<iostream>????? #include<cstring>????? #include<cstdio>????? using?namespace?std;???? const?int?MAX=110;???? #define?CLR(arr,val)?memset(arr,val,sizeof(arr))????? int?n,M[MAX],DP[MAX][MAX],s[MAX][MAX];???? void?Matrix()???? {???for(int?r=2;r<=n;r++)???????????? ????????for(int?i=1;i<=n-r+1;i++)?????? ????????{???int?j=r+i-1;???? ????????????DP[i][j]=DP[i+1][j]+M[i-1]*M[i]*M[j];???? ????????????s[i][j]=i;??? ????????????for(int?k=i;k<j;k++)???? ????????????{???int?temp=DP[i][k]+DP[k+1][j]+M[i-1]*M[k]*M[j];???? ????????????????if(temp<DP[i][j])?? ????????????????{???s[i][j]=k;?? ????????????????????DP[i][j]=temp;?? ????????????????}?? ????????????}???? ????????}????? }???? void?Find(int?i,int?j)???? {???if(i==j)???? ????{???cout<<'A'<<i;????? ????????return?;???? ????}???? ????if(i<s[i][j])?cout<<"(";????? ????Find(i,s[i][j]);???? ????if(i<s[i][j])?cout<<")";???? ????if(s[i][j]+1<j)?cout<<"(";?????? ????Find(s[i][j]+1,j);????? ????if(s[i][j]+1<j)?cout<<")";????? }???? int?main()???? {???scanf("%d",&n);???? ????for(int?i=0;i<=n;i++)???? ????????scanf("%d",&M[i]);????? ????CLR(DP,0);???? ????Matrix();???? ????Find(1,n);???? ????cout<<endl<<DP[1][n]<<endl;????????? ????return?0;???? }????
2、NYOJ 15(括號(hào)匹配),令DP[i][j]表示字符串s的第i個(gè)字符到第j個(gè)字符需要添加的最少括號(hào)數(shù)。
①、當(dāng)i==j時(shí),只有一個(gè)符號(hào),需再添加一個(gè)字符,所以DP[i][i]=1;
②、當(dāng)i<j時(shí),若是s[i]與S[j]配對(duì),那么DP[i][j]=DP[i+1][j-1];
若是不配對(duì),那么從中任選一個(gè)字符作為分界點(diǎn),i<=k<j,就有:DP[i][j]=min(DP[i][j],DP[i][k]+DP[k][j])。具體實(shí)現(xiàn)和上例一樣:
[cpp]?view plaincopyprint?
#include<iostream>?? #include<string>?? #include<cstring>?? using?namespace?std;?? const?int?MAX=1010;?? #define?min(a,b)?(a)<(b)?(a):(b)?? #define?CLR(arr,val)?memset(arr,val,sizeof(arr))?? string?s;?? int?n,DP[MAX][MAX];?? bool?pipei(char?c1,char?c2)?? {???if(c1=='('&&c2==')'||c1=='['&&c2==']')?return?true;?? ????return?false;???? }?? int?main()?? {???cin>>n;?? ????cin.get();?? ????while(n--)?? ????{???cin>>s;?????? ????????CLR(DP,0);?? ????????int?len=s.length();?? ????????for(int?i=1;i<=len;i++)?DP[i][i]=1;?? ????????for(int?r=2;r<=len;r++)?? ????????????for(int?i=1;i<=len-r+1;i++)?? ????????????{???int?j=r+i-1;?? ????????????????DP[i][j]=MAX;??? ????????????????if(pipei(s[i-1],s[j-1]))?DP[i][j]=DP[i+1][j-1];?? ????????????????for(int?k=i;k<j;k++)?? ????????????????????DP[i][j]=min(DP[i][k]+DP[k+1][j],DP[i][j]);????? ????????????}??? ????????cout<<DP[1][len]<<endl;???????? ????}?? ????return?0;?? }??
3、石子合并:Tyvj 1055(石子合并),和這個(gè)題目類似的是Tyvj 1062(合并傻子)。
[cpp]?view plaincopyprint?
#include<iostream>?? #include<cstring>??? using?namespace?std;?? const?int?MAX=1010;?? #define?CLR(arr,val)?memset(arr,val,sizeof(arr))?? int?n,a[MAX],sum[MAX][MAX],DP[MAX][MAX];?? int?Sand()?? {???for(int?r=2;r<=n;r++)?? ????????for(int?i=1;i<=n-r+1;i++)?? ????????{???int?j=i+r-1;?? ????????????DP[i][j]=DP[i+1][j]+sum[i][j];?? ????????????for(int?k=i;k<j;k++)?? ????????????????DP[i][j]=min(DP[i][j],DP[i][k]+DP[k+1][j]+sum[i][j]);?? ????????}?? ????return?DP[1][n];??? }??? int?main()?? {???cin>>n;?? ????for(int?i=1;i<=n;i++)?? ????????cin>>a[i];?? ????CLR(DP,0);?? ????CLR(sum,0);?? ????for(int?i=1;i<=n;i++)?? ????????for(int?j=i;j<=n;j++)?? ????????????sum[i][j]=sum[i][j-1]+a[j];?????????? ????cout<<Sand()<<endl;????????????? ????return?0;?? }???
4、加分二叉樹。Tyvj 1073(加分二叉樹)。
[cpp]?view plaincopyprint?
#include<iostream>?? #include<cstring>?? using?namespace?std;?? const?int?MAX=50;?? #define?CLR(arr,val)?memset(arr,val,sizeof(arr))?? long?n,a[MAX],DP[MAX][MAX],s[MAX][MAX];?? int?flag=1;?? long?Max()?? {???for(int?r=2;r<=n;r++)?? ????????for(int?i=1;i<=n-r+1;i++)??? ????????{???int?j=i+r-1;?? ????????????DP[i][j]=DP[i+1][j]+a[i];?? ????????????s[i][j]=i;??? ????????????for(int?k=i+1;k<j;k++)?? ????????????{???int?temp=DP[i][k-1]*DP[k+1][j]+a[k];????????????? ????????????????if(temp>DP[i][j])?? ????????????????{???DP[i][j]=temp;?? ????????????????????s[i][j]=k;?? ????????????????}??? ????????????}?? ????????}?????? ????return?DP[1][n];?????? }?? void?Search(int?L,int?R)?? {???if(L>R)?return?;??? ????if(flag)?flag=0;?? ????else?cout<<"?";??? ????cout<<s[L][R];?? ????if(L==R)?return?;?? ????Search(L,s[L][R]-1);???? ????Search(s[L][R]+1,R);?? }?? int?main()?? {???cin>>n;?? ????CLR(DP,0);?? ????for(int?i=1;i<=n;i++)?? ????{???cin>>a[i];?? ????????DP[i][i]=a[i];??? ????}?? ????cout<<Max()<<endl;?? ????for(int?i=1;i<=n;i++)?? ????????s[i][i]=i;??? ????Search(1,n);?? ????cout<<endl;?? ????return?0;?? }??
題5:牢房:
描述
????????SB王國(guó)中有一個(gè)奇怪的監(jiān)獄,這個(gè)監(jiān)獄里一共有P間牢房,這些牢房一字排開,從左到右按1到P進(jìn)行編號(hào),第i間后面緊挨著第(i+1)間(最后一間除外)。現(xiàn)在有P名罪犯被關(guān)押在這P間牢房里。某一天,上級(jí)下發(fā)了一個(gè)釋放名單,要求每天釋放名單上的一個(gè)人。這可把監(jiān)獄中的看守們嚇得不輕,因?yàn)榭词貍冎?#xff0c;現(xiàn)在牢房中的P個(gè)人,可以相互之間傳話。如果某個(gè)人離開了,那么原來和這個(gè)人能夠傳上話的人都會(huì)很氣憤,導(dǎo)致他們那天會(huì)一直大吼大叫,搞得看守們很是頭疼,但是如果給這些要發(fā)火的人吃上肉,他們就會(huì)安靜。現(xiàn)在看守們想知道,如何安排釋放的順序,才能使得他們花費(fèi)的肉錢最少。1<=p<=1000,1<=Q<=100
輸入
第一行兩個(gè)數(shù)P和Q,Q表示要釋放名單上的人數(shù);
第二行Q個(gè)數(shù),表示釋放哪些人
輸出
僅一行,表示最少給多少人次送肉.
樣例輸入
20?3
3?6?14
樣例輸出
35
提示:(樣例說明)
???????先釋放14號(hào)監(jiān)獄中的罪犯,要給1到13號(hào)監(jiān)獄和15到20號(hào)監(jiān)獄中的19人送肉吃;再釋放6號(hào)監(jiān)獄中的罪犯,要給1到5號(hào)監(jiān)獄和7到13號(hào)監(jiān)獄中的12人送肉吃;最后釋放3號(hào)監(jiān)獄中的罪犯,要給1到2號(hào)監(jiān)獄和4到5號(hào)監(jiān)獄中的4人送肉吃。
分析:用DP[i][j]表示在[i,j]區(qū)間所有該被釋放的人都被釋放的最小支付代價(jià)。
若是釋放k,那么代價(jià)為DP[i][k-1]+DP[k+1][j]+(j-i),所以動(dòng)態(tài)轉(zhuǎn)移方程為:DP[i][j]=min(DP[i][k-1]+DP[k+1][j]+(j-i)).
[cpp]?view plaincopyprint?
#include<iostream>?? #include<cstring>?? #include<cstdio>?? #include<algorithm>??? using?namespace?std;?? const?int?MAX=1010;?? const?int?Inf=100000000;?? int?n,m,a[MAX],sum[MAX],DP[MAX][MAX];?? int?main()?? {???for(int?i=0;i<MAX;i++)??? ????{???fill(DP[i],DP[i]+MAX,Inf);?? ????????DP[i][i]=0;??? ????}??? ????scanf("%d%d",&n,&m);?? ????for(int?i=1;i<=m;i++)?? ????????scanf("%d",&a[i]);?? ????sort(a+1,a+m+1);???? ????for(int?i=1;i<=m;i++)?? ????????sum[i]=a[i]-a[i-1]-1;?? ????sum[m+1]=n-a[m];?? ????for(int?i=1;i<=m+1;i++)?? ????????sum[i]=sum[i]+sum[i-1];?? ????for(int?i=m+1;i>=1;i--)?? ????????for(int?j=i+1;j<=m+1;j++)?? ????????????for(int?k=i;k<j;k++)?? ????????????????DP[i][j]=min(DP[i][j],DP[i][k]+DP[k+1][j]+sum[j]-sum[i-1]+j-i-1);???????????? ????printf("%d\n",DP[1][m+1]);?? ????return?0;?? }??
總結(jié)
以上是生活随笔為你收集整理的区间动态规划的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。