Vijos1451圆环取数[环形DP|区间DP]
背景
小K攢足了路費來到了教主所在的宮殿門前,但是當小K要進去的時候,卻發現了要與教主守護者進行一個特殊的游戲,只有取到了最大值才能進去Orz教主……
描述
守護者拿出被劃分為n個格子的一個圓環,每個格子上都有一個正整數,并且定義兩個格子的距離為兩個格子之間的格子數的最小值。環的圓心處固定了一個指針,一開始指向了圓環上的某一個格子,你可以取下指針所指的那個格子里的數以及與這個格子距離不大于k的格子的數,取一個數的代價即這個數的值。指針是可以轉動的,每次轉動可以將指針由一個格子轉向其相鄰的格子,且代價為圓環上還剩下的數的最大值。
現在對于給定的圓環和k,求將所有數取完所有數的最小代價。
格式
輸入格式
輸入文件cirque.in的第1行有兩個正整數n和k,描述了圓環上的格子數與取數的范圍。
第2行有n個正整數,按順時針方向描述了圓環上每個格子上的數,且指針一開始指向了第1個數字所在的格子。
所有整數之間用一個空格隔開,且不超過10000。
輸出格式
輸出文件cirque.out僅包括1個整數,為取完所有數的最小代價。
樣例1
樣例輸入1[復制]
6 1 4 1 2 3 1 3樣例輸出1[復制]
21限制
對于20%的數據,n≤10,k≤3;
對于40%的數據,n≤100,k≤10;
對于60%的數據,n≤500,k≤20;
對于100%的數據,n≤2000,k≤500;
時限1s。
提示
如上圖所示,第一步不轉動指針,取走4、3兩個數,代價為7;
第2步指針順時針轉動2格,圓環上最大數為3,代價為6,取走1、2、3兩個數,代價為6;
第3步指針順時針轉動1格,代價為1,取走剩下的一個數1,代價為1;
最小代價為7+6+6+1+1=21。
--------------------------------------------------
該死該死該死該死該死該死該死該死該死該死該死該死該死該死該死該死該死該死該死該死該死
取數的代價一定,只考慮轉移
貪心發現能取就取一定不丟最優解,從1開始,剩下的一定一直是一段區間
然后我就開始做死了f[i][j][0/1]表示剩下[i,j]這段區間,0表示指針在i-k-1處,1表示指針在j+k+1處,調了三四個小時也沒調出來
看人家的題解,i和j都來表示左/右“取了幾個數”
然而我發現,他們的意思更準確的是左右的指針跳到了哪里
轉移如代碼,至于哪個>0否則INF,好像是簡化了一下情況,不會丟失
該死該死該死
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> using namespace std; typedef long long ll; const int N=2005,INF=1e9; int n,k,a[N],f[N][N][2],ans=INF,sum=0; int mx[N][16]; void initRMQ(int n){for(int i=1;i<=n;i++) mx[i][0]=a[i];for(int j=1;j<=12;j++)for(int i=1;i+(1<<j)-1<=n;i++)mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]); } int RMQ(int l,int r){if(l>r) return 0;int k=log(r-l+1)/log(2);return max(mx[l][k],mx[r-(1<<k)+1][k]); } void dp(){f[1][n][0]=f[1][n][1]=0;for(int i=0;i<=n;i++)for(int j=0;j<=n-i;j++){if(i==0&&j==0) continue;int m=RMQ(i+k+1,n-k-j);if(i>0) f[i][j][0]=min(f[i-1][j][0]+m,f[i-1][j][1]+m*(i+j));else f[i][j][0]=INF;m=RMQ(i+k+2,n-k-j+1);if(j>0) f[i][j][1]=min(f[i][j-1][1]+m,f[i][j-1][0]+m*(i+j));else f[i][j][1]=INF;} } int main(int argc, const char * argv[]) {scanf("%d%d",&n,&k);for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];initRMQ(n);dp();for(int i=1;i<=n;i++) ans=min(ans,min(f[i][n-i][1],f[i][n-i][0]));cout<<ans+sum;return 0; }?
?
總結
以上是生活随笔為你收集整理的Vijos1451圆环取数[环形DP|区间DP]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces 703C Chri
- 下一篇: Elasticsearch1.x 基于l