The 2019 Asia Nanchang First Round Online Programming Contest C. Hello 2019(动态dp)
题意:要找到一个字符串里面存在子序列9102 而不存在8102 输出最小修改次数
思路:对于单次询问 我们可以直接区间dpOn求出最小修改次数 但是对于多次询问 我在大部分题解看到的解释一般是用线段树维护每一个区间段 每一个区间上挂一个dp[5][5]的矩阵 表示从i状态转移到j状态索要的最小修改次数 转移的时候就可以像区间dp一样 通过两个子区间合并 跟新答案。
#include <bits/stdc++.h> using namespace std; const double pi = acos(-1.0); const int N = 2e5+7; const int inf = 0x3f3f3f3f; const double eps = 1e-6; typedef long long ll; const ll mod = 10007; char s[N]; struct tree{ int dp[5][5]; }t[N<<2]; int L[N<<2],R[N<<2]; tree mul(tree a,tree b){ tree tmp; memset(tmp.dp,inf,sizeof(tmp.dp)); for(int i=0;i<5;i++) for(int j=0;j<5;j++) for(int k=0;k<5;k++) tmp.dp[i][j]=min(tmp.dp[i][j],a.dp[i][k]+b.dp[k][j]); return tmp; } void build(int p,int l,int r){ L[p]=l; R[p]=r; // memset(t[p].dp,inf,sizeof(t[p].dp)); if(l==r){ memset(t[p].dp,inf,sizeof(t[p].dp)); for(int i=0;i<5;i++) t[p].dp[i][i]=0; if(s[l]=='2'){ t[p].dp[0][0]=1; t[p].dp[0][1]=0; }else if(s[l]=='0'){ t[p].dp[1][1]=1; t[p].dp[1][2]=0; }else if(s[l]=='1'){ t[p].dp[2][2]=1; t[p].dp[2][3]=0; }else if(s[l]=='9'){ t[p].dp[3][3]=1; t[p].dp[3][4]=0; }else if(s[l]=='8'){ t[p].dp[3][3]=1; t[p].dp[4][4]=1; } return ; } int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); t[p]=mul(t[p<<1],t[p<<1|1]); } tree query(int p,int l,int r){ if(l<=L[p]&&R[p]<=r){ return t[p]; } int mid=(L[p]+R[p])>>1; tree ans1,ans2; memset(ans1.dp,inf,sizeof(ans1.dp)); memset(ans2.dp,inf,sizeof(ans2.dp)); if(r<=mid) return query(p<<1,l,r); if(l>mid) return query(p<<1|1,l,r); return mul(query(p<<1,l,r),query(p<<1|1,l,r)); } int main(){ // ios::sync_with_stdio(false); // cin.tie(0); cout.tie(0); int n,Q; scanf("%d%d",&n,&Q); scanf("%s",s+1); reverse(s+1,s+1+n); // printf("%s\n",s+1); build(1,1,n); // cout<<t[1].dp[0][4]<<endl; for(int i=1;i<=Q;i++){ int l,r; scanf("%d%d",&l,&r); // cout<<n-r+1<<" "<<n-l+1<<endl; tree ans=query(1,n-r+1,n-l+1); printf("%d\n",ans.dp[0][4]==inf?-1:ans.dp[0][4]); } }