1. 程式人生 > >【BZOJ】3971 [WF2013]Матрёшка

【BZOJ】3971 [WF2013]Матрёшка

數組 .html ide html cstring str n) div x優化

【算法】區間DP

【題解】

參考寫法:BZOJ 3971 Матрёшка 解題報告

第二個DP可以預處理mex優化到O(nM+n2),不過我懶……

第一個DP有另一種寫法:不預處理,在一個n2取出來的的區間中,枚舉決策點從左到右時,保留左最小值的可保留數不嚴格單調遞增,保留右最小值的可保留數不嚴格單調遞減,均攤O(1)。

???
【細節】

f[0]=0初始化

inf+inf+inf就會爆int

區間第二重循環是i=1...n-p,否則有可能爆數組邊界。

技術分享
#include<cstdio>
#include<algorithm>
#include<cstring>
using
namespace std; const int maxn=510,inf=0x3f3f3f3f; int dp[maxn][maxn],f[maxn],sum[maxn][maxn],ms[maxn][maxn],n,m,A[maxn]; int calc(int a,int b,int c,int d) { int m1=ms[a][b],m2=ms[c][d]; int ans=inf; ans=(b-a+1)-(sum[b][m2]-sum[a-1][m2])+(d-c+1); ans=min(ans,(d-c+1)-(sum[d][m1]-sum[c-1
][m1])+(b-a+1)); return ans; } bool B[maxn]; bool mex(int a,int b) { memset(B,0,sizeof(B)); for(int i=a;i<=b;i++)if(B[A[i]])return 0;else B[A[i]]++; bool ok=0; if(!B[1])return 0; for(int i=1;i<=m;i++) { if(ok&&B[i])return 0; if(B[i-1]&&!B[i])ok=1
; } return 1; } int main() { scanf("%d",&n); m=0; for(int i=1;i<=n;i++){scanf("%d",&A[i]);m=max(m,A[i]+1);} for(int i=1;i<=n;i++) { for(int j=1;j<=A[i];j++)sum[i][j]=sum[i-1][j]; for(int j=A[i];j<m;j++)sum[i][j]=sum[i-1][j]+1; ms[i][i]=A[i]; for(int j=i+1;j<=n;j++)ms[i][j]=min(ms[i][j-1],A[j]); } for(int p=1;p<=n-1;p++) { for(int i=1;i<=n-p;i++)// { int j=i+p; dp[i][j]=inf; for(int k=i;k<j;k++)if(dp[i][k]<inf&&dp[k+1][j]<inf)// dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+calc(i,k,k+1,j)); } } memset(f,0x3f,sizeof(f)); f[0]=0;//初始化!!! for(int i=1;i<=n;i++) { for(int j=0;j<i;j++)if(f[j]<inf-100) { if(mex(j+1,i)) { f[i]=min(f[i],f[j]+dp[j+1][i]); } } } if(f[n]>inf-100)printf("Impossible"); else printf("%d",f[n]); return 0; }
View Code

【BZOJ】3971 [WF2013]Матрёшка