bzoj千题计划235:bzoj2448: 挖油
http://www.lydsy.com/JudgeOnline/problem.php?id=2448
一遍过,嘎嘎嘎嘎嘎嘎嘎嘎嘎嘎嘎嘎,O(∩_∩)O~
题意是最小化最大值
设计区间dp
dp[i][j] 表示在能确定x不在区间[i,j]内,或确定x在区间[i,j]内某个位置的最坏情况下的最小值
dp[i][j]=min { max(dp[i][k-1],dp[k+1][j] ) + a[k] } k∈[i,j]
O(n^3)复杂度
#include<cstdio> #include<cstring> #include<iostream> using namespace std; #define N 2002 int a[N]; int dp[N][N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int main() { int n; read(n); for(int i=1;i<=n;++i) read(a[i]); memset(dp,63,sizeof(dp)); for(int i=1;i<=n;++i) dp[i][i]=a[i],dp[i][i-1]=0; for(int i=n;i;--i) for(int j=i;j<=n;++j) for(int k=i;k<=j;++k) dp[i][j]=min(dp[i][j],max(dp[i][k-1],dp[k+1][j])+a[k]); printf("%d",dp[1][n]); }
优化:
把max去掉,就可以使用单调队列优化了
很显然的结论是长区间的dp值一定>=它的子区间的dp值
当固定i和j时,随着k的递增,dp[i][k-1]单调不降,dp[k+1][j]单调不增
所以一定可以找到一个分界点g,
当k∈[i,g]时,dp[i][k-1]>dp[k+1][j]
当k∈[g+1,j]时,dp[k+1][j]>dp[i][k-1]
所以上述转移方程变为
当k∈[i,g]时,dp[i][j]=min { dp[i][k-1]+a[k] }
当k∈[g+1,j]时,dp[i][j]=min { dp[k+1][j]+a[k] }
用g[i][j]表示对于每对i,j 求出的g
对于 当k∈[i,g[i][j] ]时,dp[i][j]=min { dp[i][k-1]+a[k] }
可以得出结论 g[a][b]<=g[a][b+1]
因为这里是前面的dp值>后面的dp值,[a,b+1]在[a,b]后面加了一个位置,
后面的dp值不变或增大,前面要包含更多的位置使前面变的更大,才能>=后面,所以g的位置不变或后移
所以固定区间左端点,随右端点的右移,g单调不减
对于 当k∈[g[i][j]+1,j]时,dp[i][j]=min { dp[k+1][j]+a[k] }
可以得出结论 g[a-1][b]<=g[a][b]
因为这里是后面的dp值>前面的dp值,[a-1,b]在[a,b]前面加了一个一个位置
前面的dp值不变或增大,后面要包含更多的位置使后面变的更大,才能>=前面,所以g的位置不变或前移
所以固定区间右端点,随左端点的左移,g单调不增
所以可维护n+1个单调队列
一个表示固定左端点,n个表示固定右端点
因为是左端点从n倒序枚举,右端点从左端点正序枚举
所以固定左端点的只需要用一个,左端点改变的时候清空队列即可
但是右端点是跳来跳去的,所以要用n个
实际实现的时候可以不计算g
如果要计算g的话,由上面可以得出结论
g[a-1][b]<=g[a][b]<=g[a][b+1]
利用g的单调性计算g
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 2002 int a[N]; int dp[N][N]; int q[N][N]; int h[N],t[N]; #define A(x) dp[i][(x)-1]+a[(x)] #define B(x) dp[(x)+1][j]+a[(x)] void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int main() { int n; read(n); for(int i=1;i<=n;++i) read(a[i]); memset(dp,63,sizeof(dp)); for(int i=n;i;--i) { dp[i][i]=a[i]; h[0]=0; q[0][t[0]=1]=i; q[i][t[i]=1]=i; for(int j=i+1;j<=n;++j) { while(h[0]<t[0] && A(q[0][h[0]])<B(q[0][h[0]])) ++h[0]; while(h[0]<t[0] && A(j)<A(q[0][t[0]-1])) --t[0]; q[0][t[0]++]=j; while(h[j]<t[j] && B(q[j][h[j]])<A(q[j][h[j]])) ++h[j]; while(h[j]<t[j] && B(i)<B(q[j][t[j]-1])) --t[j]; q[j][t[j]++]=i; dp[i][j]=min(A(q[0][h[0]]),B(q[j][h[j]])); } } printf("%d",dp[1][n]); }
2448: 挖油
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 133 Solved: 57
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
8 24 12 6
Sample Output
HINT
对于100%的数据,n<=2000,ti<=10^6