竞赛同行dp优化总结

“由于这几道题目有很大的共同点,就放在一起解析”
P1576
此题就是先推出暴力的dp,首先就要想到+1和-1其实是一件事,就是求差值
a[]表示原序列b[]表示排序后的序列
f[i][j]表示把前i个数变成非降序列,并且第i个数变成原序列中从小到大第j个数所用的最少步数。
用g[i][j]表示最优的f[i][j];(i不变)
由于f[][]和g[][]互相传递,所以可以用同一个数组来存储
f[i][j]=min(f[i-1][j]+abs(a[i]-b[j]),f[i][j-1]);
由于空间的问题还要把这个改成滚动数组;

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,a[5001],b[5001];
ll f[5001];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            f[j]+=abs(a[i]-b[j]);
            if(j>1) f[j]=min(f[j],f[j-1]);
        }
    printf("%lld\n",f[n]);
}

P1697
暴力dp[i][j][k]=min(dp[i-1][j][k]+c[i][k]);dp[i][j][k]=min(dp[i-1][j-1][v],c[i][k])
效率n^3,所以说可以过但是不佳;所以需要优化和上题一样
先用一个 l[i][j][k],r[i][j][k]分别表示k的左端和右端的最优值;
然后互相转移,由于不是自己推的优化,讲解起来并不深入(本人才疏学浅),
希望见谅

#include<bits/stdc++.h>
#define INF 1e14
#define N 105
#define ll long long
using namespace std;
int n,m,Q,k,i,j,a[N],c[N][N],id[N][N];
ll f[N][N][N],l[N][N],r[N][N];
int main(){
    scanf("%d%d%d",&n,&m,&Q);
    for(i=1;i<=n;i++) 
        scanf("%d",&a[i]);
    for(i=1;i<=n;i++) 
        for(j=1;j<=m;j++) 
            scanf("%d",&c[i][j]);
    for(i=0;i<=n;i++)
        for(j=1;j<=m;j++)
            for(k=1;k<=Q;k++) 
                f[i][j][k]=INF;
    for(i=0;i<=n;i++) 
        for(k=0;k<=Q;k++) 
            l[i][k]=r[i][k]=INF;
    l[0][0]=0;
    for(i=1;i<=n;i++) {
        for(j=1;j<=m;j++)
            for(k=1;k<=Q;k++){
                int cost=c[i][j];
                if(a[i]==j) cost=0;
                if(a[i]!=0&&j!=a[i]) continue;
                f[i][j][k]=min(f[i][j][k],f[i-1][j][k]+cost);
                if(id[i-1][k-1]==j) 
                    f[i][j][k]=min(f[i][j][k],r[i-1][k-1]+cost);
                else
                    f[i][j][k]=min(f[i][j][k],l[i-1][k-1]+cost);
            }
        for(k=1;k<=Q;k++)
            for(j=1;j<=m;j++){
                if(f[i][j][k]<l[i][k]){
                    r[i][k]=l[i][k];
                    l[i][k]=f[i][j][k];
                    id[i][k]=j;
                }
                else
                    if(f[i][j][k]<r[i][k]) 
                        r[i][k]=f[i][j][k];
            }
    }    
    ll ans=INF;
    for(i=1;i<=m;i++) ans=min(ans,f[n][i][Q]);
    if(ans==INF)printf("-1");
    else printf("%lld",ans);
    return 0;
}

P2125
这题的优化和上面的雷同,但是效率还是不够(N^3)
所以还是需要加一个hash的比较,用hash离散后的字符串二分比较到最后的
不相同的部分,如果最后还有不同的就二分继续比较,只有一个的时候,就可以直接比较计算答案了

#include<bits/stdc++.h>
#define N 5005
#define mod 1000000007
#define ll long long
using namespace std;
int n,cnt,f[N][N],h[N][N],sum[N][N],ans;
char s[N];
bool cmp(int a, int b, int c, int d) {
    if(s[a]!=s[c]) return s[a]>s[c];
    if(h[a][b]==h[c][d]) return 0;
    int l=1,r=b-a,x=0;
    while(l<=r) {
        int mid=l+r>>1;
        if(h[a][a+mid]==h[c][c+mid]) 
            l=mid+1;
        else{
            x=mid;
            r=mid-1;
        }
    }
    return s[a+x]>s[c+x];
}
int main(){
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
         h[i][j]=(11LL*(ll)h[i][j-1]+s[j]-'0')%mod;
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
            sum[i][j]=1;
    for(int i=1;i<=n;i++) f[1][i]=1;
    for(int i=1;i<=n;i++){
        if(s[i]=='0')
            for(int j=i;j<=n;j++)
                sum[i][j]=sum[i-1][j];
        else
            for(int j=i;j<=n;j++){
                int k=max(i+i-j-1,0);
                ll x=(ll)sum[i-1][i-1]-sum[k][i-1]; 
                if(k>0&&cmp(i,j,k,i-1))
                    x=(x+f[k][i-1])%mod;
                if(x<0)x+=mod;
                f[i][j]=((ll)f[i][j]+x)%mod;
                sum[i][j]=((ll)sum[i-1][j]+f[i][j])%mod;
            }
    }
    printf("%d",sum[n][n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36316033/article/details/80462615