日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

区间动态规划

發布時間:2025/6/15 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 区间动态规划 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、矩陣連乘,tyvj 1198(最優矩陣連乘),關鍵是寫出動態轉移方程。用DP[i][j]表示矩陣Ai乘到Aj的最優解,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的列)。那么我只需要模舉這其中的組合然后取最小值即可,這個時候的動態轉移方程為: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]的值。

思想是先求出相鄰組的連乘次數,如一開始算相鄰兩個相乘,如圖:

那么每兩個相乘的結果我們可以得到。同理繼續求三個相鄰的結果。

計算的話比方說要求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;?//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;//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王國中有一個奇怪的監獄,這個監獄里一共有P間牢房,這些牢房一字排開,從左到右按1到P進行編號,第i間后面緊挨著第(i+1)間(最后一間除外)。現在有P名罪犯被關押在這P間牢房里。某一天,上級下發了一個釋放名單,要求每天釋放名單上的一個人。這可把監獄中的看守們嚇得不輕,因為看守們知道,現在牢房中的P個人,可以相互之間傳話。如果某個人離開了,那么原來和這個人能夠傳上話的人都會很氣憤,導致他們那天會一直大吼大叫,搞得看守們很是頭疼,但是如果給這些要發火的人吃上肉,他們就會安靜。現在看守們想知道,如何安排釋放的順序,才能使得他們花費的肉錢最少。1<=p<=1000,1<=Q<=100

    輸入

    第一行兩個數P和Q,Q表示要釋放名單上的人數;

    第二行Q個數,表示釋放哪些人

    輸出

    僅一行,表示最少給多少人次送肉.

    樣例輸入

    20?3
    3?6?14

    樣例輸出

    35

    提示:(樣例說明)

    ???????先釋放14號監獄中的罪犯,要給1到13號監獄和15到20號監獄中的19人送肉吃;再釋放6號監獄中的罪犯,要給1到5號監獄和7到13號監獄中的12人送肉吃;最后釋放3號監獄中的罪犯,要給1到2號監獄和4到5號監獄中的4人送肉吃。

    分析:用DP[i][j]表示在[i,j]區間所有該被釋放的人都被釋放的最小支付代價。

    若是釋放k,那么代價為DP[i][k-1]+DP[k+1][j]+(j-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;??
  • }??
  • 總結

    以上是生活随笔為你收集整理的区间动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。