• DP——由蒟蒻到神犇的进阶之路


    开始更新咯

    DP专题【题目来源BZOJ】

    一、树形DP

    1.bzoj2286消耗战

    题解:因为是树形结构,一个点与根节点不联通,删一条边即可,

       于是我们就可以简化这棵树,把有用的信息建立一颗虚树,然后开始DP即可

      1 /*
      2 思路: 
      3 */
      4 #include<algorithm>
      5 #include<cstdio>
      6 #include<cmath>
      7 #include<iostream>
      8 #include<cstring>
      9 #define ll long long 
     10 #define inf 1e60
     11 #define N 250005    
     12 #define M 550005
     13 using namespace std;
     14 int n,m,k,tot,top,tmp,ind;
     15 int a[N];
     16 int now[N],pre[M],v[M],val[M];
     17 int now1[N],pre1[M],v1[M];
     18 int fa[N][22],deep[N],dfn[N],bin[22];
     19 int z[M],st[M];
     20 ll f[N],mx[N];
     21 int read()
     22 {
     23     int x=0,f=1; char ch; 
     24     while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
     25     while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9'); 
     26     return x*f;
     27 }
     28 void ins(int a,int b,int c){
     29     ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c;
     30 }
     31 void insert(int a,int b){
     32     if (a==b) return; 
     33     ++tot; pre1[tot]=now1[a]; now1[a]=tot; v1[tot]=b;
     34 }  
     35 void dfs(int x)
     36 {
     37     dfn[x]=++ind;
     38     for (int i=1; bin[i]<=deep[x]; i++)
     39         fa[x][i]=fa[fa[x][i-1]][i-1];
     40     for (int p=now[x]; p; p=pre[p])
     41     {
     42         int son=v[p];
     43         if (son==fa[x][0]) continue;
     44         deep[son]=deep[x]+1;
     45         mx[son]=min(mx[x],(ll)val[p]);
     46         fa[son][0]=x;
     47         dfs(son);
     48     }        
     49 }
     50 int lca(int x,int y)
     51 {
     52     if(deep[x]<deep[y]) swap(x,y);
     53     int t=deep[x]-deep[y];
     54     for (int i=0; bin[i]<=t; i++) 
     55     if (bin[i]&t) x=fa[x][i];
     56     for (int i=19; i>=0; i--)
     57         if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
     58     if (x==y) return x; return fa[x][0];
     59 }
     60 void prework()
     61 {
     62     bin[0]=1;
     63     for (int i=1; i<=21; i++) bin[i]=bin[i-1]*2;
     64      tmp=0; mx[1]=inf; dfs(1);
     65 }
     66 bool cmp(int a,int b){return dfn[a]<dfn[b];
     67 }
     68 void work()
     69 {
     70     sort(a+1,a+k+1,cmp);
     71     top=tot=0;
     72     z[1]=a[1]; top++;
     73     for (int i=2; i<=k; i++) if (lca(z[top],a[i])!=z[top]) z[++top]=a[i];
     74     st[1]=1; tmp=1;
     75     for (int i=1; i<=top; i++)
     76     {
     77         int now=z[i],f=lca(now,st[tmp]);
     78         for (;;)
     79         {
     80             if (deep[f]>=deep[st[tmp-1]])
     81             {
     82                 insert(f,st[tmp--]);
     83                 if (st[tmp]!=f) st[++tmp]=f; 
     84                 break;
     85             }
     86             insert(st[tmp-1],st[tmp]); tmp--;
     87         }
     88         if (st[tmp]!=now) st[++tmp]=now;
     89      }
     90      while (--tmp) insert(st[tmp],st[tmp+1]);
     91 }
     92 void dp(int x)
     93 {
     94     f[x]=mx[x]; ll sum=0;
     95     for (int p=now1[x]; p; p=pre1[p]) 
     96     {
     97         int son=v1[p]; dp(son); sum+=f[son];
     98     }
     99     now1[x]=0;
    100     if (sum==0) f[x]=mx[x]; else f[x]=min(f[x],sum);
    101 }
    102 void init()
    103 {
    104     n=read();
    105     for (int i=1; i<n; i++)
    106     {
    107         int u=read(),v=read(),val=read();
    108         ins(u,v,val); ins(v,u,val);
    109     }
    110 //    m=read();
    111     prework();
    112 }
    113 void solve()
    114 {
    115     m=read();
    116     for (int i=1; i<=m; i++) 
    117     {
    118         k=read(); for (int j=1; j<=k; j++) a[j]=read(); work();
    119         dp(1); printf("%lld
    ",f[1]);
    120     }
    121 }
    122 int main()
    123 {
    124     init();
    125     solve();
    126     return 0;
    127 }
    View Code

    2.bzoj1827奶牛大集会

    题解:树形DP,先以1号点为根节点,然后再次树形DP求出其他节点为根的答案即可

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<cstdio>
     6 #define ll long long 
     7 #define inf 1e60
     8 #define N  200005
     9 #define M  500005
    10 using namespace std;
    11 int n,m;
    12 int now[N],pre[M],v[M],val[M],tot;
    13 ll f[N];
    14 int size[N],num[N],dis[N];
    15 ll ans,sum;
    16 void ins(int a,int b,int c)
    17 {
    18     ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c;
    19 }
    20 int read()
    21 {
    22     int x=0,f=1; char ch; 
    23     while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
    24     while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
    25     return x*f; 
    26 }
    27 void init()
    28 {
    29     n=read(); 
    30     for (int i=1; i<=n; i++) num[i]=read(),sum+=num[i];
    31     for (int i=1; i<n; i++)
    32     {
    33         int u=read(),v=read(),val=read();
    34         ins(u,v,val); ins(v,u,val);
    35     }
    36 }
    37 void dfs(int x,int fa)
    38 {
    39     size[x]=num[x];
    40     for (int p=now[x]; p; p=pre[p])
    41     {
    42         int son=v[p];
    43         if (son==fa) continue;
    44         dis[son]=dis[x]+val[p];
    45         f[son]=1ll*dis[son]*num[son];
    46         dfs(son,x);
    47         size[x]+=size[son];
    48         f[x]+=f[son];
    49     }
    50 }
    51 void treedp(int x,int fa)
    52 {
    53     for (int p=now[x]; p; p=pre[p])
    54     {
    55         int son=v[p];
    56         if (son==fa) continue;
    57         f[son]=f[x]-1ll*size[son]*val[p]+1ll*(sum-size[son])*val[p];
    58         treedp(son,x);
    59     }
    60 }
    61 void solve()
    62 {
    63     dfs(1,0);
    64     //cout<<" "<<sum<<endl;
    65     treedp(1,0);
    66     ans=inf;
    67     for (int i=1; i<=n; i++) ans=min(ans,f[i]);
    68     printf("%lld
    ",ans);
    69 }
    70 int main()
    71 {
    72     init(); 
    73     solve();
    74 }
    75 /*
    76     
    77 */
    View Code

    3.bzoj1596电话网络

    题解:树形DP,f[i][1]代表以i为根的子树都被覆盖的代价且在i放,[0]为都被覆盖i不放,

       [2]为子树被覆盖,i没被覆盖的代价

       f[i][1]=min(f[son][0],f[son][1],f[son][2])+1;

       f[i][0]=最小的f[son][1]+min(f[son][0],f[son][1]);

       f[x][2]+=f[son][0];

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #define ll long long 
    #define inf 100000000
    #define N  10005
    #define M  30005
    using namespace std;
    int n;
    int pre[M],v[M],now[N],tot;
    int f[N][3];
    void ins(int a,int b){++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b;
    }
    int read()
    {
        int x=0,f=1; char ch; 
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f; 
    }
    void dfs(int x,int fa)
    {
        int sum=0;
        f[x][1]=1; f[x][0]=inf;
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p];
            if (son==fa) continue;
            dfs(son,x);
            f[x][1]+=min(f[son][0],min(f[son][1],f[son][2]));
            f[x][2]+=f[son][0];
        }
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p]; if (son==fa) continue;
            int who=son; sum=f[son][1];
            for (int j=now[x]; j; j=pre[j]) if ((v[j]!=fa) && (v[j]!=who)) sum+=min(f[v[j]][0],f[v[j]][1]);
            f[x][0]=min(f[x][0],sum);
        }
    }
    int main()
    {
        n=read();
        for (int i=1; i<n; i++)
        {
            int u=read(),v=read(); 
            ins(u,v); ins(v,u);
        }
        dfs(1,0);
        printf("%d
    ",min(f[1][0],f[1][1]));
    }
    View Code

    4.bzoj1864三色二叉树

    题解:f[i][0]代表i为绿色的答案,f[i][1]为i为其他颜色的答案

       f[i][0]=max(f[ls[i]][0]+f[rs[i]][1],f[ls[i]][1]+f[rs[i]][0]);

       f[i][1]=f[ls[i]][0]+f[rs[i]][1]+1;

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #define ll long long 
    #define inf 1000000000
    #define N 300005
    using namespace std;
    int ls[N],rs[N];
    int n,m,size,ans1,ans2;
    int f[N][2];
    void read(int x)
    {
        char ch=getchar();
        if (ch=='0') return;
        if (ch=='1') ls[x]=++size,read(ls[x]);
        if (ch=='2') ls[x]=++size,read(ls[x]),rs[x]=++size,read(rs[x]);
    }
    void dp1(int x)
    {
        if (!x) return ;
        dp1(ls[x]); dp1(rs[x]);
        f[x][1]=f[ls[x]][0]+f[rs[x]][0]+1;
        f[x][0]=max(f[ls[x]][0]+f[rs[x]][1],f[ls[x]][1]+f[rs[x]][0]);
    }
    void dp2(int x)
    {
        if (!x) return;
        dp2(ls[x]); dp2(rs[x]);
        f[x][1]=f[ls[x]][0]+f[rs[x]][0]+1;
        f[x][0]=min(f[ls[x]][0]+f[rs[x]][1],f[ls[x]][1]+f[rs[x]][0]);
    }
    void solve()
    {
        dp1(1); ans1=max(f[1][0],f[1][1]);
        memset(f,0,sizeof(f));
        dp2(1); ans2=min(f[1][0],f[1][1]);
        printf("%d %d
    ",ans1,ans2);
    }
    int main()
    {
        read(++size);
        solve();
    }
    View Code

    5.bzoj1060时态同步

    题解:f[i]代表以i为子树的儿子时间同步所需长度 

       ans+=maxsonf-(f[son]+val[p])

       :val[p]为边权,maxsonf为i为子树的最长的长度

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 500005
    #define M 1000005
    using namespace std;
    int now[N],pre[M],v[M],val[M],f[N];
    int n,m,s,tot;
    ll ans;
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void ins(int a,int b,int c)
    {
        ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c;
    }
    void work(int x,int fa)
    {
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p];
            if (son==fa) continue;
            work(son,x);
            f[x]=max(f[x],f[son]+val[p]);
        }
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p];
            if (son==fa) continue;
            ans+=f[x]-(f[son]+val[p]);
        }
    }
    int main()
    {
        n=read();
        s=read();
        for (int i=1; i<n; i++)
        {
            int u=read(),v=read(),val=read();
            ins(u,v,val); ins(v,u,val);
        }
        work(s,0); printf("%lld
    ",ans);
    }
    View Code

    6.bzoj2435道路修建

    题解:ans+=val[p]*size[i]*(n-size[i])  size[i]为i为根的子树大小

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 1000005
    #define M 2000005
    using namespace std;
    int now[N],pre[M],v[M],val[M],size[N];
    int n,tot;
    ll ans;
    
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void ins(int a,int b,int c)
    {
        ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b; val[tot]=c;
    }
    void dfs(int x,int fa)
    {
        size[x]=1;
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p];
            if (son==fa) continue;
            dfs(son,x); size[x]+=size[son];
            ans+=1ll*val[p]*(abs(n-size[son]-size[son]));
        }
    }
    int main()
    {
        n=read();
        for (int i=1; i<n; i++)
        {
            int u=read(),v=read(),val=read();
            ins(u,v,val); ins(v,u,val);
        }
        dfs(1,0);
        printf("%lld
    ",ans);
    }
    View Code

    7.bzoj2427软件安装

    题解:缩点+反边+dp【详见代码】

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 2000000000000
    #define N 205
    #define M 605
    using namespace std;
    int n,m;
    int cost[N],val[N],depend[N],belong[N];
    struct data{
        int tot,pre[M],now[N],v[M];
    }a,b;
    int tot,dfn[N],low[N],z[N],id;
    bool vis[N],in[N];
    int sc[N],sv[N],type;
    int f[N][M];
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void ins1(int u,int v)
    {
    //    cout<<" "<<u<<" "<<v<<endl;
        ++a.tot; a.pre[a.tot]=a.now[u]; a.now[u]=a.tot; a.v[a.tot]=v;
    }
    void ins2(int u,int v)
    {
        //cout<<" "<<u<<" "<<v<<endl;
        ++b.tot; b.pre[b.tot]=b.now[u]; b.now[u]=b.tot; b.v[b.tot]=v; 
    }
    void tarjan(int x){
        int son=0;
        dfn[x]=low[x]=++id;
        z[++tot]=x; vis[x]=true;
        for (int p=a.now[x]; p; p=a.pre[p])
        {
            son=a.v[p];
            if (!dfn[son])
            {
                tarjan(son); low[x]=min(low[x],low[son]);
            } else if (vis[son]) low[x]=min(low[x],dfn[son]);
        }
        if (low[x]==dfn[x])
        {
            ++type;
            while (son!=x) 
            {
                son=z[tot--]; vis[son]=false; belong[son]=type; 
                sv[type]+=val[son]; sc[type]+=cost[son];    
            }    
        }
    }
    void init_pre()
    {
        n=read(); m=read();
        for (int i=1; i<=n; i++) cost[i]=read();
        for (int i=1; i<=n; i++) val[i]=read();
        for (int i=1; i<=n; i++)
        {
            int x=read();
            if(x)ins1(x,i);
        }
        for (int i=1; i<=n; i++)
        {
            if (!dfn[i]) tarjan(i);
        }
        for (int i=1; i<=n; i++)
        {
            for (int p=a.now[i]; p; p=a.pre[p])
            {
                int son=a.v[p];
                if (belong[son]!=belong[i]) ins2(belong[i],belong[son]),in[belong[son]]=true;
            }
        }
        for (int i=1; i<=type; i++)
        {
            if (!in[i]) ins2(type+1,i);
        }
        type++;
    }
    void dp(int x)
    {
        for (int p=b.now[x]; p; p=b.pre[p])
        {
            int son=b.v[p];
            dp(son);
            for (int j=m-sc[x]; j>=0; j--) 
                for (int k=0; k<=j; k++) f[x][j]=max(f[x][j],f[x][j-k]+f[son][k]);
        }
        for (int j=m; j>=0; j--)
        {
            if (j>=sc[x]) f[x][j]=f[x][j-sc[x]]+sv[x]; else f[x][j]=0;
        }
    }
    void solve()
    {
        dp(type);
        printf("%d
    ",f[type][m]);
    }
    int main()
    {
        init_pre();
        solve();
    }
    View Code

    8.bzoj1369GEM

    题解:开始智障了,以为只有整棵树为1或2的情况,于是GG了,实践证明数值个数不会超过5.

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 10005  
    #define M 20005 
    using namespace std;
    int n,m,tot;
    int pre[M],v[M],now[N];
    int f[N][21];
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void ins(int a,int b) {++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b;
    }
    void dp(int x,int fa)
    {
        for (int i=1; i<=5; i++) f[x][i]=i;
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p];
            if (son==fa) continue;
            dp(son,x);
        }
        for (int i=1; i<=5; i++)
        {
            for (int p=now[x]; p; p=pre[p])
            {
                int son=v[p]; if (son==fa) continue; int sum=inf;
                for (int j=1; j<=5; j++)
                {
                    if (j==i) continue;
                    sum=min(sum,f[son][j]);
                }
                f[x][i]+=sum;
            }
        }
    }
    void solve()
    {
        dp(1,0); int ans=inf;
        for (int i=1; i<=5; i++) ans=min(ans,f[1][i]); printf("%d
    ",ans);
            
    }
    int main()
    {
        n=read();
        for (int i=1; i<n; i++)
        {
            int u=read(),v=read(); ins(u,v); ins(v,u);
        }
        solve(); 
    }
    View Code

    9.bzoj3573米特运输

    题解:可以发现一个特点,当我们确定一个点的时候,整棵树都可以确定,于是我们枚举留下留哪一个点且记录根的值,n-一样的 即为答案

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 500005
    #define M 1000005
    using namespace std;
    int n;
    int pre[M],v[M],now[N],a[N];
    double s[N],val[N];
    int in[N];
    int tot,tmp,ans;
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void ins(int a,int b)
    {
        ++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b;
    }
    void dp(int x,int fa)
    {
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p];
            if (son==fa) continue;
            s[son]=s[x]+log(in[x]);
            dp(son,x);
        } 
    }
    int main()
    {
        n=read();
        for (int i=1; i<=n; i++) a[i]=read();
        for (int i=1; i<n; i++)
        {
            int u=read(),v=read(); ins(u,v); ins(v,u);
            in[u]++; in[v]++;
        }
        for (int i=2; i<=n; i++) in[i]--;
        s[1]=log(1);
        dp(1,0);
        for (int i=1; i<=n; i++)
        {
            val[i]=s[i]+log(a[i]);
        }
        sort(val+1,val+n+1); ans=n*2; tmp=1;
        for (int i=2; i<=n; i++)
        {
            if (val[i]-val[i-1]<1e-5) tmp++;
            else tmp=1;
            ans=min(ans,n-tmp);
        }
    
        printf("%d
    ",ans);
    }
    View Code

    10.bzoj3522Hotel

    题解:因为我没妹子所以就不屑于写这种题【虐狗啊】

       枚举节点,以这个节点为中心,于是我们只要知道到这个节点的距离为J的有多少,然后组合排列的转换,搞一搞就好了

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 5005
    #define M 10005
    using namespace std;
    int pre[M],v[M],now[N];
    int tmp[N],dis[N],s1[N],s2[N];
    int tot,n,m,mx;
    ll ans;
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void ins(int a,int b){++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b;
    }
    void dp(int x,int fa)
    {
        tmp[dis[x]]++; mx=max(mx,dis[x]);
        for (int p=now[x]; p; p=pre[p])
        {
            int son=v[p]; if (son==fa) continue;
            dis[son]=dis[x]+1;
            dp(son,x);            
        }
    }
    int main()
    {
        n=read();
        for (int i=1; i<n; i++)
        {
            int u=read(),v=read();
            ins(u,v); ins(v,u);
        }
        ans=0;
        
        for (int i=1; i<=n; i++)
        {
            memset(s1,0,sizeof(s1));
            memset(s2,0,sizeof(s2));
            for (int p=now[i]; p; p=pre[p])
            {
                int son=v[p];
                mx=dis[son]=1; dp(son,i);
                for (int j=1; j<=mx; j++)
                {
                    ans+=s2[j]*tmp[j];
                    s2[j]+=tmp[j]*s1[j];
                    s1[j]+=tmp[j];
                    tmp[j]=0;
                }
            }
        }
        printf("%lld
    ",ans);
    }
    View Code

     小结:在树形DP过程中当我们不需要那些没有节点我们可以利用虚树来解决;

        树形DP一般是在一棵树上的,且状态较多,贪心无法解决的问题;

        树形DP一般可以看出,但是转移方程需要细想;

    二、区间动规

    1.bzoj1260涂色

    题解:f[i][j]为i--j的涂色最小值

       f[i][j]=f[i+1][j]+a[i+1]==a[i]

           =f[i][j-1]+a[j-1]==a[j]

           =f[i+1][j-1](a[i]==a[j]==a[i+1]==a[j-1])

           =f[i+1][j-1]+(a[i]==a[j]) [ a[i+1]!=a[i]||a[j]!=a[j-1] ]

       或者:a[i]==a[j] 

          则:f[i][j]=min(f[i+1][j],f[i][j-1])

            f[i][j]=f[i+1][j-1]+1;

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 100
    using namespace std;
    char s[N];
    int f[N][N];
    int n,ans;
    void dp()
    {
        for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) f[i][j]=inf/2;
        for (int i=1; i<=n; i++) f[i][i]=1;
        for (int len=1; len<n; len++)
        {
            for (int i=1; i<=n; i++)
            {
                int j=i+len;
                if (j>n) break;
                if (s[i]==s[j])
                {
                    if (len==1) f[i][j]=1;
                    else
                    {
                        f[i][j]=min(f[i+1][j],f[i][j-1]);
                        f[i][j]=min(f[i][j],f[i+1][j-1]+1);
                    }
                 }
                 else
                 {
                     for (int k=i; k<j; k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);    
                }
            }
        }
    }
    int main()
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        dp();
        printf("%d
    ",f[1][n]);
    }
    View Code

    2.bzoj1090字符串压缩

    题解:f[i][j]---->i到j被压缩的最小长度;

       f[i][j]=min(f[i][k]+f[k+1][j]);

       same(i,j)代表i到j可以被压缩,【同下题解释】

       f[i][j]=i--j的最小段数的位数+2+i--j的最小段长;

       注:他们可以被压缩,但是我们不一定要全部压缩

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define N 105
    using namespace std;
    char s[N];
    int n;
    bool mark[N][N];
    int f[N][N];
    bool same(int l,int r,int ll,int rr)
    {
        if ((r-l+1)%(rr-ll+1)!=0) return false ;
        int tmp=(rr-ll+1);
        for (int i=l; i<=r; i++)
        {
            int tmp=rr-ll+1;
            if (s[i]!=s[(i-l)%tmp+ll]) return false;
        }
        return true;
    }
    int get(int x)
    {
        int t=0;
        while (x) { x/=10; t++;
        } return t;
    }
    int dp(int l,int r)
    {
        int tmp=(r-l+1);
        if (tmp==1) return tmp;
        if (mark[l][r]) return f[l][r];
        mark[l][r]=1;
        for (int i=l; i<r; i++)
        {
            tmp=min(tmp,dp(l,i)+dp(i+1,r));
            if (same(i+1,r,l,i)) tmp=min(tmp,dp(l,i)+2+get((r-i)/(i-l+1)+1));    
        }
        return f[l][r]=tmp;
    }
    int main()
    {
        scanf("%s",s+1); n=strlen(s+1);
        printf("%d
    ",dp(1,n));
    }
    View Code

    3.bzoj1068压缩

    题解:f[i][j][t]---->i到j的这一段中间插了(T==1) M的最小压缩长度;

       f[i][j][1]=min(f[i][k][1]+f[k+1][j][1]);

       f[i][j][t]=min(f[i][k][t]+j-k);

       same(i,j) f[i][j][t]=f[i][(i+j)/2][0]+1;

     1 /*
     2     f[i][j][t]---->i到j的这一段中间插了(T==1) M; 
     3 */
     4 #include<algorithm>
     5 #include<cstdio>
     6 #include<cstring>
     7 #include<cmath>
     8 #define N 105
     9 using namespace std;
    10 int n;
    11 char s[N];
    12 int f[N][N][2];
    13 bool mark[N][N][2];
    14 bool same(int l,int r)
    15 {
    16     int tmp=r-l+1;
    17     if (tmp&1) return false;
    18     for (int i=l; i<=(l+r)/2; i++)
    19     {
    20         if (s[i]!=s[i+tmp/2]) return false; 
    21     }
    22     return true;
    23 }
    24 int dp(int l,int r,int t)
    25 {
    26     int tmp=r-l+1;
    27     if (tmp==1) return 1;
    28     if (mark[l][r][t]) return f[l][r][t];
    29     mark[l][r][t]=1;
    30     if (t==1) for (int i=l; i<r; i++) tmp=min(tmp,dp(l,i,1)+dp(i+1,r,1)+1);
    31     for (int i=l; i<r; i++) tmp=min(tmp,dp(l,i,t)+r-i);
    32     if (same(l,r)) tmp=min(tmp,dp(l,(l+r)/2,0)+1);
    33     return  f[l][r][t]=tmp;
    34 } 
    35 int main()
    36 {
    37     scanf("%s",s+1);
    38     n=strlen(s+1);
    39     printf("%d
    ",dp(1,n,1));
    40 }
    View Code

     小结:区间DP一般处理区间覆盖、压缩之类问题,但是容易与数据结构弄混,

       导致代码长度大;

    三、状压

    /*

    序:因为状压dp方式类似,

      故具体方程不予列出,

      细节详见代码;

    */

     1.bzoj1725牧场的安排

    题解:f[i][j]代表第i行状态为j的情况数;

       转移见代码;

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 13
    #define M 4096
    #define base 100000000
    using namespace std;
    int f[N][M],val[N];
    int maxn,n,m,ans;
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void dp()
    {
        for (int i=0; i<=maxn; i++) 
        if((i&(i>>1))==0&&(i|val[1])==val[1])
            f[1][i]=1;//,cout<<"    "<<i<<endl;
        //    if (((i & (i>>1))==0) && (i|val[1])==val[1]) f[1][i]==1,cout<<" dd"<<endl;
        for (int i=2; i<=n; i++)
        {
            for (int j=0; j<=maxn; j++)
            {
                if (f[i-1][j])
                for (int k=0; k<=maxn; k++)
                {
                    //if (((k&(k>>1))==0) && (k|val[i])==val[i] && (k&j)==0) f[i][k]=(f[i][k]+f[i-1][j])%base,cout<<" dd "<<endl;
                    if((j&k)==0&&(k|val[i])==val[i]&&(k&(k>>1))==0)
                        f[i][k]=(f[i][k]+f[i-1][j])%base;
                }
            }
        } 
    }
    int main()
    {
        n=read(); m=read();
        for (int i=1; i<=n; i++)
        {
            for (int j=1; j<=m; j++)
            {
                int x=read();
                val[i]<<=1; val[i]+=x;
            }
        }
        maxn=(1<<m)-1;
        dp();
        for (int i=0; i<=maxn; i++)
        {
            ans=(ans+f[n][i])%base;
        }
        printf("%d
    ",ans);
    }
    View Code

    2.bzoj1087互不侵犯king

    题解:同上,具体见代码;

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 10
    #define M 525
    #define KK 9*9+5
    using namespace std;
    int n,K,maxn;
    ll ans; 
    ll f[N][M][KK];
    int t[N],num[M]; 
    bool bo[M],can[M][M];
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    void prework()
    {
        t[1]=1; for (int i=2; i<=n; i++) t[i]=t[i-1]<<1; maxn=(1<<n)-1;
        for (int i=0; i<=maxn; i++)
        {
            if (i&(i>>1)) continue;
            int x=0;
            for (int j=1; j<=n; j++) if (i & t[j]) x++;
            num[i]=x; bo[i]=1;
        }
        for (int i=0; i<=maxn; i++) if (bo[i])
            for (int j=0; j<=maxn; j++) if (bo[j])
            {
                if ((i&j)==0 && ((i<<1) & j)==0 && ((j<<1) & i)==0) can[i][j]=1;
            }
    }
    void dp()
    {
        for (int i=0; i<=maxn; i++) if (bo[i]) f[1][i][num[i]]=1;
        for (int i=1; i<n; i++)
            for (int j=0; j<=maxn; j++) if (bo[j])
                for (int k=0; k<=maxn; k++) if (bo[k])
                if (can[j][k])
                {
                    for (int p=num[j]; p+num[k]<=K; p++) f[i+1][k][p+num[k]]+=f[i][j][p];
                }
    }
    int main()
    {
        n=read(); K=read();
        prework();
        dp();
        for (int i=0; i<=maxn; i++) ans+=f[n][i][K];
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    3.bzoj1688疾病管理

    题解:f[i]疾病状态为i的最大选取牛数

       j|val[k]==i;f[i]=max(f[j|val[k]]+1);

       注:val[k]为第k头牛的疾病状态;

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define M    42768
    using namespace std;
    int n,d,k,maxn;
    int f[M],val[M];
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    bool check(int x)
    {
        int cnt=0;
        while (x) { cnt+=x&1; x>>=1;
        }
        return cnt<=k;
    }
    int main()
    {
        n=read(); d=read(); k=read();
        for (int i=1; i<=n; i++) 
        {
            int x=read(),a;
            while (x--) a=read(),val[i]|=(1<<(a-1));
        }
        maxn=(1<<d)-1;
        for (int i=1; i<=n; i++)
            for (int j=maxn; j; j--) f[j|val[i]]=max(f[j|val[i]],f[j]+1);
        int ans=0;
        for (int i=0; i<=maxn; i++) if (check(i)) ans=max(ans,f[i]); printf("%d
    ",ans);
    }
    /*
    6 3 2
    0
    1 1
    1 2
    1 3
    2 2 1
    2 2 1
    */
    View Code

    4.bzoj2073PRZ

    题解:f[i]为状态为i,本次选择重量为j的最小时间

       sc[j]<M; f[i]=min(f[i],f[j^i]+sw[j]);

       sc[j],sw[j]分别为状态为j的重量与时间

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 20
    #define M 70000
    using namespace std;
    int W,n,maxn;
    int f[M],st[M],sw[M];
    int t[N],w[N];
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    /*
        颓废死亡与坚持成功,请自己选择; 
    */
    void prework()
    {
        maxn=(1<<n)-1;
        for (int i=0; i<=maxn; i++)
        {
            int x=i,z=1;
            while (x)
            {
                if (x&1) sw[i]+=w[z],st[i]=max(st[i],t[z]);
                x>>=1; z++;
            }    
        } 
    }
    void dp()
    {
        f[0]=0;
        for (int i=1; i<=maxn; i++)
        {
            f[i]=inf/3;
            for (int j=i; j; j=i&(j-1)) if (sw[j]<=W)
            f[i]=min(f[i],f[i^j]+st[j]); 
        }
    }
    int main()
    {
        W=read(); n=read();
        for (int i=1; i<=n; i++)
        {
            t[i]=read(); w[i]=read();
        }
        prework(); 
        dp();
        printf("%d
    ",f[maxn]);
    }
    View Code

    5.bzoj1231混乱的奶牛

    题解:f[i][j]为状态为i,末尾奶牛为j的答案;

       详见代码;

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    #define N 17
    #define M 70000
    using namespace std;
    int n,m,maxn;
    int s[N],bin[N];
    ll f[M][N],ans;
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    /*
        颓废死亡与坚持成功,请自己选择; 
    */
    int main()
    {
        n=read(); m=read();
        for (int i=1; i<=n; i++) s[i]=read();
        bin[1]=1; for (int i=2; i<=n; i++) bin[i]=bin[i-1]<<1;
        for (int i=1; i<=n; i++) f[bin[i]][i]=1;
        maxn=(1<<n)-1;
        for (int i=1; i<=maxn; i++)
        {
            for (int j=1; j<=n; j++) if (i&bin[j])
            for (int k=1; k<=n; k++) if ((bin[k]|i)!=i && abs(s[j]-s[k])>m)
            f[bin[k]|i][k]+=f[i][j];
        }
        for (int i=1; i<=n; i++) ans+=f[maxn][i];
        printf("%lld
    ",ans);
    }
    View Code

    6.bzoj1072排列

    题解:f[i][j]状态为i 余数为 j的状态数

       f[i|bin[k-1]][(j*10+a[k])%d]+=f[i][j]

       最后排列组合去重;

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #define ll long long 
    #define inf 0x7ffffff
    
    using namespace std;
    int bin[20];
    int Case,d,len,maxn;
    int a[15],v[15],tot[15],f[1025][1005];
    char s[15];
    int read()
    {
        int x=0,f=1; char ch;
        while (ch=getchar(),ch<'0'||ch>'9') if (ch=='-') f=-1;
        while (x=x*10+ch-'0',ch=getchar(),ch>='0'&&ch<='9');
        return x*f;
    }
    /*
        颓废死亡与坚持成功,请自己选择; 
    */
    int main()
    {
        Case=read();
        bin[0]=1;for(int i=1;i<20;i++)bin[i]=bin[i-1]<<1;
        while (Case--)
        {
            scanf("%s",s+1); d=read();
            len=strlen(s+1); 
            for (int i=0; i<=9; i++) v[i]=1,tot[i]=0;
            for (int i=1; i<=len; i++)
            {
                a[i]=s[i]-'0'; tot[a[i]]++; v[a[i]]*=tot[a[i]];
            }
            for (int i=0; i<bin[len]; i++) 
                for (int j=0; j<d; j++) f[i][j]=0;
            f[0][0]=1; 
            for(int i=0;i<bin[len];i++)
            for(int k=0;k<d;k++)
                if(f[i][k])
                    for(int x=1;x<=len;x++)
                        if((bin[x-1]&i)==0)
                            f[i|bin[x-1]][(a[x]+k*10)%d]+=f[i][k];
            for (int i=0; i<=9; i++) f[bin[len]-1][0]/=v[i];
            printf("%d
    ",f[bin[len]-1][0]);    
        }
    }
    View Code

    小结:一般看到数据在20左右,就马上要想到状压DP;

    四、斜率优化

    1.bzoj1911特别行动队

    题解:

    待更------------------------

       

      

  • 相关阅读:
    poi隐藏列
    凯西太太的果园
    java中不可变对象深入理解
    excel添加空白行的快捷键
    如何在多个页面中,引入一个公共组件
    对后端返回的数据进行适配
    我与时间管理的故事
    在前端团队的那些日子(初见)
    我是这样做时间管理的(下)
    我是这样做时间管理的(上)
  • 原文地址:https://www.cnblogs.com/HQHQ/p/5714737.html
Copyright © 2020-2023  润新知