zoukankan      html  css  js  c++  java
  • 复习动规(1)

    今天是7月13号,现在打算冲国银。

    从现在到国赛前,每天会复习或学习知识点。

    今天是复习动态规划。

    状压DP

    第一道,[NOI2001]炮兵阵地很明显也很好写的状压dp。

    dp[i][j][k]是第i行状态为j,第i-1行状态为k的方案数,再枚举一重状态即可。挺经典。

    第二道,[SDOI2009]Bill的挑战定义的数组稍微有点不一样。

    预处理时有个辅助数组f[i][j],指第i位的字符可以为j的字符串的集合。

    dp[i][j]指的是当前做到字符串第i位,满足条件的字符串的集合为j的方案数。

    最后求值的时候要判断j中1的个数刚好等于k。

    第三道,[NOI2015]寿司晚宴原题可转化为“将2~n中的部分数字分为两组,一个组中的所有数的质因数集合与另一个组的质因数集合无交集”。

    这样我们就可以想到设dp[i][j]指A组的质因数集合为i,B组的质因数集合为j(i&j!=0)。再遍历每个数可以放入A组或B组或不放。

    然后发现每个数大于22的质因数至多一个,把它拎出并用它排序,保证大于22的质因数相同的一些数在一起。

    再设fa[i][j],fb[i][j],初值设为dp[i][j],定义和dp[i][j]一样,但是一个专门放入A组,一个专门放入B组。

    质因数相同的一些数弄完之后,dp[i][j]=fa[i][j]+fb[i][j]-dp[i][j]。因为fa和fb是包括了dp初值的,所以dp[i][j]=专门放入A组+专门放入B组+不放入任何一组。

    具体看代码实现:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int n,p[11]={2,3,5,7,11,13,17,19};
    ll P,ans,dp[261][261],fa[261][261],fb[261][261];
    struct nod{
        int s,h;
    }a[510];
    bool operator>(nod aa,nod bb){
        return aa.h<bb.h;
    }
    bool operator<(nod aa,nod bb){
        return aa.h>bb.h;
    }
    int main(){ int x;
        scanf("%d%lld",&n,&P);
        for(int i=2;i<=n;i++){ x=i;
            for(int j=0;j<8;j++) if(!(x%p[j])){
                a[i].s|=(1<<j);
                while(!(x%p[j])) x/=p[j];
            }
            a[i].h=x;
        }
        sort(a+2,a+1+n);
        dp[0][0]=1;
        for(int i=2;i<=n;i++){
            if(a[i].h!=a[i-1].h||a[i].h==1){
                memcpy(fa,dp,sizeof(dp));
                memcpy(fb,dp,sizeof(dp));
            }
            for(int j=255,t;j>=0;j--)
                for(int k=j;k>=0;k=(k-1)&j){ t=(j^k);//这里k是枚举j的子集,于是j^k和k无交集 
                    if(!(a[i].s&k)) fa[t|a[i].s][k]=(fa[t|a[i].s][k]+fa[t][k])%P;
                    if(!(a[i].s&t)) fb[t][k|a[i].s]=(fb[t][k|a[i].s]+fb[t][k])%P;
                    if(!k) break;
                }
            if(a[i].h!=a[i+1].h||a[i].h==1)
                for(int j=255,t;j>=0;j--)
                    for(int k=j;k>=0;k=(k-1)&j){ t=(j^k);
                        dp[t][k]=(fa[t][k]+fb[t][k]-dp[t][k]+P)%P;
                        if(!k) break;
                    }
        }
        for(int j=255;j>=0;j--)
            for(int k=j;k>=0;k=(k-1)&j){
                ans=(ans+dp[j^k][k])%P;
                if(!k) break;
            }
        printf("%lld
    ",ans);
        return 0;
    }
    [NOI2015]寿司晚宴

    倍增优化DP

    第一道,跑路是有点水,好像和动规没关系?先把一步能到达的路连起来再floyd就完事了。

    单调性优化DP

    第一道,[NOI2011]NOI 嘉年华

    首先将时间离散化,并设f[i][j],g[i][j],表示1~i的时间内第一个嘉年华有j个活动时的第二个嘉年华最大活动数,i~T的时间内第一个嘉年华有j个活动时的第二个嘉年华最大活动数。

    再预处理d[i][j]指i~j时间内的所有活动数,即可O(n3)dp出f[i][j],g[i][j],其中max{min(j,f[T][j])}为第一个答案。

    要使第i个活动必须举办,则设dp[i][j]为i~j时间内的活动必须举办的最佳答案,第i+1个答案就是max{dp[l][r]}(1<=l<=s[i],s[i]+t[i]<=r<=T)。

    接下来只要求出dp[l][r]就可以了,可知dp[l][r]=max{min(x+y+d[l][r],f[l][x]+g[r][y])}。(x指l左边的在第一个嘉年华的活动数,y指r右边的在第一个嘉年华的活动数)

    乍一看要O(n4),事实上发现x越大,y的最佳值越小,所以利用单调性就可以优化一重时间复杂度。对了,要注意细节。

    具体看代码实现:

    #include<bits/stdc++.h>
    using namespace std;
    int n,ct,ans,s[210],t[210],b[410],d[410][410],f[410][210],g[410][210],dp[410][410];
    map<int,int>q;
    int Max(int x,int y) {return x>y?x:y;}
    int Min(int x,int y) {return x<y?x:y;}
    int rd(){
        int s=0,ff=1;
        char w=getchar();
        while(!isdigit(w))
            ff^=w=='-',w=getchar();
        while(isdigit(w))
            s=s*10+w-'0',w=getchar();
        return ff?s:-s;
    }
    int main(){
        n=rd(); int y;
        for(int i=1;i<=n;i++)
            s[i]=rd(),t[i]=s[i]+rd(),b[i*2-1]=s[i],b[i*2]=t[i];
        sort(b+1,b+1+n*2);
        for(int i=1;i<=n*2;i++)
            if(b[i]!=b[i-1]||i==1)
                q[b[i]]=++ct;
        for(int i=1;i<=n;i++) s[i]=q[s[i]],t[i]=q[t[i]];
        for(int i=1;i<=ct;i++)
            for(int j=i;j<=ct;j++)
                for(int k=1;k<=n;k++)
                    if(i<=s[k]&&t[k]<=j) d[i][j]++;
        memset(f,-127/3,sizeof(f));
        memset(g,-127/3,sizeof(g));
        f[1][0]=0;
        for(int i=2;i<=ct;i++)
            for(int j=0;j<=n;j++)
                for(int k=1;k<i;k++)
                    f[i][j]=Max(f[i][j],Max(f[k][Max(j-d[k][i],0)],f[k][j]+d[k][i]));
        for(int j=0;j<=n;j++)
            ans=Max(ans,Min(j,f[ct][j]));
        printf("%d
    ",ans);
        g[ct][0]=0;
        for(int i=ct-1;i>=1;i--)
            for(int j=0;j<=n;j++)
                for(int k=i+1;k<=ct;k++)
                    g[i][j]=Max(g[i][j],Max(g[k][Max(j-d[i][k],0)],g[k][j]+d[i][k]));
        for(int l=1;l<=ct;l++)
            for(int r=l+1;r<=ct;r++){ y=n;
                while(y&&g[r][y]<0) y--;
                for(int x=0;x<=n;x++){ if(f[l][x]<0) break;
                    while(y&&Min(x+y+d[l][r]-1,f[l][x]+g[r][y-1])>=Min(x+y+d[l][r],f[l][x]+g[r][y])) y--;//注意这里取等号 
                    dp[l][r]=Max(dp[l][r],Min(x+y+d[l][r],f[l][x]+g[r][y]));
                }
            }
        for(int i=1;i<=n;i++){ ans=0;
            for(int l=1;l<=s[i];l++)
                for(int r=t[i];r<=ct;r++)
                    ans=Max(ans,dp[l][r]);
            printf("%d
    ",ans);
        }
        return 0;
    }
    [NOI2011]NOI嘉年华

    今天就写了这么多,明天加油吧。

  • 相关阅读:
    蓝牙低功耗(Bluetooth Low Energy)
    Android 蓝牙(概述)
    Android 学习笔记之 Activity 简介
    Android 学习笔记之常用控件
    Android 学习笔记之界面布局
    委托和事件(C#)
    Java 资源汇总
    如何阅读英文原版教材
    Combobox 控件绑定数据
    《将博客搬至CSDN》
  • 原文地址:https://www.cnblogs.com/manmanjiangQwQ/p/13293101.html
Copyright © 2011-2022 走看看