[NOIP2014]子矩阵解题报告

这题看上去没啥思路,一看数据范围小的可怜,就算了下暴搜的时间复杂度 O(C(16,8)(C(16,8)+m3))108 似乎没啥问题,然后就写了个暴搜,出了点数据发现T了,又加了个最优化剪枝然后就A了。。
结果一看人家的代码全是DP。。让我有点蛋疼。
其实只要把暴搜的后半部分一改就可以了,把二维压成一维后,就有一个显然的DP方程:处理出选一列的代价 lsi ,选一对列的代价 hs(i,j) ,设 f(i,j) 为选第i列,在第i列之前已经选了j-1列的最小代价,则显然

f(i,j)=lsi+min1k<i{ f(k,j1)+hs(k,i) }
这道题虽然是普及组的题,但是也挺好的,让我明白把二维压到一维与直接在二维上做的巨大差别!这种思想以后我在处理矩阵的时候一定要时刻牢记!

= =这里实际上还有一个问题,就是暴搜出所有组合的时间复杂度是多少。我们在n个数中选择m个,我们采用从前往后暴搜01串的搜索方式,并加上这样两个剪枝:如果当前搜出的1的个数大于m,剪枝;如果即使剩下的数的个数全选1也不足以凑齐m个,则剪枝。那么我们试图分析一下这两个剪枝的效果,第一个剪枝好说,第二个剪枝的话,我们考虑在还剩m-x个时它减掉的叶节点数目(要求它不能被还剩m-(x-1)时减掉),那么可以得到这样一个显然的式子:

O(0imCin1imCi1n(mi)12mi)
,这玩意儿看起来似乎是相当大的。。但是枚举一些m和n可以发现它就等于 Cmn ,也就是说 暴搜出每个组合的时间是O(1)的!
这应该说是一个相当惊人的结论了,我们尝试证明这个结论,即证明等式
0imCin1imCi1n(mi)12mi=Cmn
,我们对其进行变形:
0i<mCin1imCi1n(mi)12mi=0
——①注意到右边的 Cmn 其实在左边过了,所以我们干脆把它剪掉好。
0i<mCin=0i<mCinm+i2mi1

——②注意到这两个 似乎是可以使用相同的范围的,所以对于后面的 ,我们以i+1代i,令i的范围由[1,m]推到,[0,m)
但是2的幂和组合数乘起来了,我并不会处理这种东西,这让我很蛋疼;不过如果我们把它们放到杨辉三角中,发现右边是在左边上面的一条有系数的斜线,于是我们以第n行系数全为1开始向上推,这时我们惊奇的发现第n-1行的系数是2…21(m-1个2),而边界正是左边的第一项,而第n-2行的系数是4…42(m-2个4),于是我们似乎便可以以这样不严谨的方式证明了上述神奇的时间复杂度了!
Code(dfs):

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,r,c;
int a[20][20];
int ls[20],hs[20][20];
int list[20];
int ans=0x7fffffff;
inline void ldfs(int x,int last,int nowc,int nowsum){
    if(m-x<nowc||nowsum>=ans)return;
    if(x==m){
        ans=min(ans,nowsum);
        return;
    }
    ldfs(x+1,last,nowc,nowsum);
    if(nowc)ldfs(x+1,x,nowc-1,nowsum+ls[x]+hs[last][x]);
}
inline void hdfs(int x,int now){
    if(n-x<now)return;
    if(x==n){
        int i,j;
        memset(ls,0,sizeof(ls));
        for(i=m;i--;)
            for(j=r;--j;)
                ls[i]+=abs(a[list[j]][i]-a[list[j-1]][i]);
        int k;
        memset(hs,0,sizeof(hs));
        for(i=0;i<m;++i)
            for(j=i+1;j<m;++j)
                for(k=r;k--;)
                    hs[i][j]+=abs(a[list[k]][i]-a[list[k]][j]);
        ldfs(0,m-1,c,0);
        return;
    }
    hdfs(x+1,now);
    if(now){
        list[--now]=x;
        hdfs(x+1,now);
    }
}
int main(){
    freopen("submatrix.in","r",stdin);
    freopen("submatrix.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&r,&c);
    int i,j;
    for(i=0;i<n;++i)
        for(j=0;j<m;++j)
            scanf("%d",a[i]+j);
    hdfs(0,r);
    printf("%d\n",ans);
}

Code(DP):

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,r,c;
int a[20][20];
int ls[20],hs[20][20];
int list[20];
int ans=0x7fffffff;
int f[20][20];
inline void ldfs(int x,int last,int nowc,int nowsum){
    int i,j;
    for(i=0;i<m;++i)
        for(j=0;j<=c;++j)
            f[i][j]=1E9;
    for(i=0;i<m;++i)f[0][0]=0,f[i][1]=ls[i];
    int k;
    for(i=1;i<m;++i)
        for(j=2;j<=c;++j){
            for(k=0;k<i;++k)f[i][j]=min(f[i][j],f[k][j-1]+hs[k][i]);
            f[i][j]+=ls[i];
        }
    for(i=0;i<m;++i)ans=min(ans,f[i][c]);
}
inline void hdfs(int x,int now){
    if(n-x<now)return;
    if(x==n){
        int i,j;
        memset(ls,0,sizeof(ls));
        for(i=m;i--;)
            for(j=r;--j;)
                ls[i]+=abs(a[list[j]][i]-a[list[j-1]][i]);
        int k;
        memset(hs,0,sizeof(hs));
        for(i=0;i<m;++i)
            for(j=i+1;j<m;++j)
                for(k=r;k--;)
                    hs[i][j]+=abs(a[list[k]][i]-a[list[k]][j]);
        ldfs(0,m-1,c,0);
        return;
    }
    hdfs(x+1,now);
    if(now){
        list[--now]=x;
        hdfs(x+1,now);
    }
}
int main(){
    freopen("submatrix.in","r",stdin);
    freopen("submatrix.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&r,&c);
    int i,j;
    for(i=0;i<n;++i)
        for(j=0;j<m;++j)
            scanf("%d",a[i]+j);
    hdfs(0,r);
    printf("%d\n",ans);
}

你可能感兴趣的:(dp,DFS)