[NOIP2014]子矩阵

1812. [NOIP2014]子矩阵

http://www.cogs.pro/cogs/problem/problem.php?pid=1812

★★★   输入文件:submatrix.in   输出文件:submatrix.out   简单对比
时间限制:1 s   内存限制:256 MB

【题目描述】

最暴力的算法是枚举选择哪些行、列。复杂度为O(C(n,r)*C(m,c))。不过显然不能承受。(C为组合数)
注意到虽然O(C(n,r)*C(m,c))不能承受,但O(C(n,r))或O(C(m,c))是可以接受的。
不妨考虑枚举其中一个(假设枚举行)。
枚举完行后,由于行已确定,因此可以把所有行捆绑,视为一个整体。
处理处列与列之间的价值,然后可以用动态规划解决这个问题。
设dp[i][k]表示前i列选了k列,并且第i列强制被选。那么转移方程为:dp[i][k]=dp[j][k-1]+cost[j][i]+val[i],其中j<i,cost[j][i]表示第i列与第j列相邻的花费,val[i]表示第i列内的花费。
答案即为min{dp[i][c]}。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
int a[20][20],m,n,r,c,lr[20],ud[20],dp[20][20];
int rlc[20],udc[20][20],ans=0x7fffffff;
void Dp(){
    memset(rlc,0,sizeof(rlc));
    memset(udc,0,sizeof(udc));
    memset(dp,127/3,sizeof(dp));
    //不同列之间的差 
    for(int i=1;i<=m;i++)
        for(int j=1;j<i;j++)
            for(int k=1;k<=r;k++)
                udc[j][i]+=abs(a[lr[k]][i]-a[lr[k]][j]);
    //不同行之间的差 
    for(int i=1;i<=m;i++)
        for(int j=1;j<r;j++)
            rlc[i]+=abs(a[lr[j]][i]-a[lr[j+1]][i]);
    for(int i=1;i<=n;i++)dp[i][0]=0,dp[i][1]=rlc[i];
    for(int i=1;i<=c;i++){
        for(int j=i;j<=m;j++){
            for(int k=i-1;k<j;k++){
                dp[j][i]=min(dp[k][i-1]+udc[k][j]+rlc[j],dp[j][i]);
            }
        }
    }
    for(int i=c;i<=m;i++)ans=min(ans,dp[i][c]);
}
void dfs(int Step,int rest){
    if(Step==r){Dp();return;}
    if(r-Step>rest)return;
    for(int i=rest;i>=1;i--){lr[Step+1]=i;dfs(Step+1,i-1);}
}
int main(){
    freopen("submatrix.in","r",stdin);
    freopen("submatrix.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&r,&c);
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
    dfs(0,n);
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/thmyl/p/6816028.html