生活随笔
收集整理的這篇文章主要介紹了
区间动态规划
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1、矩陣連乘,tyvj 1198(最優(yōu)矩陣連乘),關鍵是寫出動態(tài)轉移方程。用DP[i][j]表示矩陣Ai乘到Aj的最優(yōu)解,P[]用來存儲矩陣的行和列,M[i-1]表示矩陣i的行,M[i]表示矩陣i的列。
當i==j時,DP[i][i]=0;
當i<j時,可以假設他從k位置隔開。比方說從A3..A8。那么A1A2(A3A4A5A6A7A8)A9。我們知道若A矩陣為r1*c的矩陣,A2為c*r2的矩陣,得到的將是一個r1*r2的矩陣,且連乘次數為r1*c*r2。假設我從5位置隔開,就可以分為A1A2((A3A4A5)(A6A7A8))A9。那么這個時候的次數為:DP[3][5]+DP[6][8]+M[2]*M[5]*M[8](A3的行*A5的列*A8的列)。那么我只需要模舉這其中的組合然后取最小值即可,這個時候的動態(tài)轉移方程為: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]的值。
思想是先求出相鄰組的連乘次數,如一開始算相鄰兩個相乘,如圖:
那么每兩個相乘的結果我們可以得到。同理繼續(xù)求三個相鄰的結果。
計算的話比方說要求A1A2A3,可以化為(A1)(A2A3)=DP[1][1]+DP[2][3]+M[0]*M[1]*M[3],依次類推結果為:DP[i][j]=DP[i][i]+DP[i+1][j]+M[i-1]*M[i]*M[j]。
只有這樣做后我才可以求任意組合的次數,求出三個一組的結果后,我就可以求任意的四個矩陣連乘的最小值。如:A4A5A6A7,隨便怎么組合,通過上面是計算都可以求出相應的結果。
現在討論下范圍:用r保存要求的每每相鄰的組數(2<=r<=n)(從2開始是這里前提條件是i<j),i的范圍為:(1<=i<=n-r+1,稍作推理就知道,當r=3時,即三個三個一組時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(括號匹配),令DP[i][j]表示字符串s的第i個字符到第j個字符需要添加的最少括號數。
①、當i==j時,只有一個符號,需再添加一個字符,所以DP[i][i]=1;
②、當i<j時,若是s[i]與S[j]配對,那么DP[i][j]=DP[i+1][j-1];
若是不配對,那么從中任選一個字符作為分界點,i<=k<j,就有:DP[i][j]=min(DP[i][j],DP[i][k]+DP[k][j])。具體實現和上例一樣:
[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(石子合并),和這個題目類似的是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王國中有一個奇怪的監(jiān)獄,這個監(jiān)獄里一共有P間牢房,這些牢房一字排開,從左到右按1到P進行編號,第i間后面緊挨著第(i+1)間(最后一間除外)?,F在有P名罪犯被關押在這P間牢房里。某一天,上級下發(fā)了一個釋放名單,要求每天釋放名單上的一個人。這可把監(jiān)獄中的看守們嚇得不輕,因為看守們知道,現在牢房中的P個人,可以相互之間傳話。如果某個人離開了,那么原來和這個人能夠傳上話的人都會很氣憤,導致他們那天會一直大吼大叫,搞得看守們很是頭疼,但是如果給這些要發(fā)火的人吃上肉,他們就會安靜?,F在看守們想知道,如何安排釋放的順序,才能使得他們花費的肉錢最少。1<=p<=1000,1<=Q<=100
輸入
第一行兩個數P和Q,Q表示要釋放名單上的人數;
第二行Q個數,表示釋放哪些人
輸出
僅一行,表示最少給多少人次送肉.
樣例輸入
20?3
3?6?14
樣例輸出
35
提示:(樣例說明)
???????先釋放14號監(jiān)獄中的罪犯,要給1到13號監(jiān)獄和15到20號監(jiān)獄中的19人送肉吃;再釋放6號監(jiān)獄中的罪犯,要給1到5號監(jiān)獄和7到13號監(jiān)獄中的12人送肉吃;最后釋放3號監(jiān)獄中的罪犯,要給1到2號監(jiān)獄和4到5號監(jiān)獄中的4人送肉吃。
分析:用DP[i][j]表示在[i,j]區(qū)間所有該被釋放的人都被釋放的最小支付代價。
若是釋放k,那么代價為DP[i][k-1]+DP[k+1][j]+(j-i),所以動態(tài)轉移方程為: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;?? }??
總結
以上是生活随笔為你收集整理的区间动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。