题解 洛谷P2258 【子矩阵】

应该很容易想到暴力骗分。

我们考虑暴力\(dfs\)枚举所有行的选择,列的选择,每次跑一遍记下分值即可。

时间复杂度:\(O(C_n^r \times C_m^c \times r \times c)\)

可以水过\(60pts\)

#include<bits/stdc++.h>
#define INF 1000000007
using namespace std;
inline int read(){
    register int s=0,f=1;
    register char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f*=-1;ch=getchar();}
    while(isdigit(ch))s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
    return s*f;
}
const int N=20;
int n,m,r,c,ans=INF;
int a[N][N],fh[N],fl[N];
int solve(){
    int sum=0;
    for(int i=1;i<=r;i++){
        for(int j=1;j<c;j++){
            sum+=abs(a[fh[i]][fl[j]]-a[fh[i]][fl[j+1]]);
        }
    }
    for(int j=1;j<=c;j++){
        for(int i=1;i<r;i++){
            sum+=abs(a[fh[i]][fl[j]]-a[fh[i+1]][fl[j]]);
        }
    }
    return sum;
}
void dfsl(int dep,int cnt){
    if(cnt>c){ans=min(ans,solve());return;}
    if(dep>m)return;
    fl[cnt]=dep;dfsl(dep+1,cnt+1);
    fl[cnt]=0;dfsl(dep+1,cnt);
}
void dfsh(int dep,int cnt){
    if(cnt>r){dfsl(1,1);return;}
    if(dep>n)return;
    fh[cnt]=dep;dfsh(dep+1,cnt+1);
    fh[cnt]=0;dfsh(dep+1,cnt);
}
int main(){
    ios::sync_with_stdio(false);
    n=read(),m=read(),r=read(),c=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j]=read();
        }
    }
    dfsh(1,1);
    cout<<ans<<endl;
    return 0;
}

【算法分析】

我们依然先来枚举行的选择,于是接下来问题就转化成了:

  • 一个\(r \times m\)的矩阵选\(c\)列,使其分值最小。

显然,这是一个\(01\)选择列的问题,所以我们直接\(dp\)就好了。

设状态\(dp[i][j]\)表示当前选择第\(i\)列的同时已经选择了\(j\)列的最小分数。

接下来我们预处理好两个东西:

  • \(up[i]\)表示在当前行选择下第\(i\)列的总分值。

  • \(num[i][j]\)表示在当前行选择下连接第\(i\)列和第\(j\)列的分值。

    扫描二维码关注公众号,回复: 8278371 查看本文章

那么对于第\(dp[i][j]\),我们只需再枚举一个第\(k\)列。当连接第\(k\)列和第\(i\)列时,价值即为本身第\(i\)列的分值\(up[i]\)和连接两列的分值\(num[i][k]\)

于是不难推出:

\[dp[i][j]=min\{ dp[k][j-1]+up[i]+num[k][i] \} \]

\[i \in [1,m],j\in[2,min(i,c)],k \in[1,i)\]

边界处理:

\[dp=\{0x7f\},dp[i][1]=up[i]\]

更新最小分值:

\[ans=min\{dp[i][c]\}\]

时间复杂度:\(O(C_n^r \times m^2n)\)

#include<bits/stdc++.h>
#define INF 1000000007
using namespace std;
inline int read(){
    register int s=0,f=1;
    register char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f*=-1;ch=getchar();}
    while(isdigit(ch))s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
    return s*f;
}
const int N=20;
int n,m,r,c,ans=INF;
int a[N][N],f[N],up[N],num[N][N],dp[N][N];
void solve(){
    memset(up,0,sizeof(up));
    memset(num,0,sizeof(num));
    for(int j=1;j<=m;j++){
        for(int i=1;i<r;i++){
            up[j]+=abs(a[f[i]][j]-a[f[i+1]][j]);
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=i+1;j<=m;j++){
            for(int k=1;k<=r;k++){
                num[i][j]+=abs(a[f[k]][i]-a[f[k]][j]);
            }
        }
    }
    memset(dp,0x7f,sizeof(dp));
    int tot=INF;
    for(int i=1;i<=m;i++){
        dp[i][1]=up[i];
        for(int j=2;j<=min(i,c);j++){
            for(int k=1;k<i;k++){
                dp[i][j]=min(dp[i][j],dp[k][j-1]+up[i]+num[k][i]);
            }
        }
        tot=min(tot,dp[i][c]);
    }
    ans=min(ans,tot);
}
void dfs(int dep,int cnt){
    if(cnt>r){solve();return;}
    if(dep>n)return;
    f[cnt]=dep;dfs(dep+1,cnt+1);
    f[cnt]=0;dfs(dep+1,cnt);
}
int main(){
    ios::sync_with_stdio(false);
    n=read(),m=read(),r=read(),c=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j]=read();
        }
    }
    dfs(1,1);
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Agonim/p/12080820.html