zoukankan      html  css  js  c++  java
  • 【Luogu】P2258子矩阵(状态压缩,DP)

    233今天蒟蒻我连文化课都没听光想着这个

    然后我调了一下午终于过了!!!

    一看数据范围似乎是状压,然而216等于65536。开一个65536*65536的二维数组似乎不太现实。

    所以Rqy在四月还是几月给我们讲这道题的时候说要半DFS半DP,时间复杂度O(2n*n3)

    怎么个半DFS半DP法呢?

    其实我没DFS。所以这个问题不重要。

    我真的没用DFS。枚举从1到2n-1的所有集合,把二进制数中1的个数不等于r的都筛掉。然后对于每个状态,预处理出每一列内部的代价,预处理出列与列之间的代价,然后进行DP。

    那DP状态怎么设计呢?

    我们设f[i][j]表示考虑前i列,选择j列,且必须选择第i列的最小代价。则转移方程为f[i][j]=min(f[i][j],f[k][j-1]+第i列内部代价+第k列和第i列之间的代价) 1<=k<j

    所以必须选择第i列的道理就是转移方程好设计。如果没有这个限制,可能你在算第k列和第i列之间的代价的时候第k列根本没被选上,结果就WA。

    最后ans=min(ans,f[i][c])  1<=i<=m

    代码如下

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<cctype>
    #define Max ((1<<n)-1)
    inline long long read(){
        long long num=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)){
            if(ch=='-')    f=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            num=num*10+ch-'0';
            ch=getchar();
        }
        return num*f;
    }
    inline long long min(long long a,long long b){    return a<b?a:b;    }
    int que[1000][1000];
    int f[20][20];
    int d[20],w[20][20];
    bool s[100];
    int ans=0x7fffffff;
    inline int count(int x){
        int ans=0;
        while(x){
            x&=(x-1);
            ans++;
        }
        return ans;
    }
    
    int main(){
        int n=read(),m=read(),r=read(),c=read();
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)    que[i][j]=read();
        for(int i=1;i<=Max;++i){
            if(count(i)!=r)    continue;
            memset(d,0,sizeof(d));
            memset(f,127/3,sizeof(f));
            memset(w,0,sizeof(w));
            int x=i;
            for(int j=1;j<=n;++j){
                s[j]=x&1;
                x>>=1;
            }
            for(int j=1;j<=m;++j)
                for(int k=1;k<=n;++k)
                    if(s[k]){
                        for(int u=1;u<j;++u)
                            w[j][u]+=std::abs(que[k][j]-que[k][u]);
                        int l=k-1;
                        while(!s[l]&&l)    l--;
                        if(!l)    continue;
                        d[j]+=std::abs(que[k][j]-que[l][j]);
                    }
            for(int j=1;j<=m;++j){
                f[j][0]=0;
                f[j][1]=d[j];
            }
            for(int j=1;j<=m;++j)
                for(int k=1;k<=j&&k<=c;++k){
                    for(int l=1;l<j;++l){
                        if(f[j][k-1]==f[0][0])    continue;
                        f[j][k]=min(f[j][k],f[l][k-1]+w[j][l]+d[j]);
                    }
                }
            for(int j=c;j<=m;++j)    ans=min(ans,f[j][c]);
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    获取文件mime类型
    PHP的CURL
    PHP curl报错“Problem (2) in the Chunked-Encoded data”解决方案
    MySQL中的group_concat函数
    MYSQL批量修改表前缀与表名sql语句
    ubuntu18.04 无法连接有线
    ffmpeg接收udp输入的h264文件流,推流到rtmp服务器
    nginx-rtmp
    tf.image.crop_and_resize
    tf.reduce_sum
  • 原文地址:https://www.cnblogs.com/cellular-automaton/p/7491598.html
Copyright © 2011-2022 走看看