题解:子矩阵(NOIP2014普及组T4)

又是dp

暴力枚举会T

考虑先固定一个变量,比如先枚举行

然后预处理每行之间的绝对值,每列之间的绝对值

然后dp进行转移

注意记录选择的行数

转移记得加上新选的列的行之间的绝对值,即w[i],

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstring>
 5 using namespace std;
 6 int n, m, r, c;
 7 int a[20][20], f[20][20];//f[i][j]表示选择了i列,最后一列为j
 8 int w[20], v[20][20], path[20];
 9 int ans=1<<30;
10 void work(){
11     memset(f,127,sizeof(f));
12     memset(w,0,sizeof(w));
13     memset(v,0,sizeof(v)); 
14     for(int i=1; i<=m; i++) 
15         for(int j=1; j<r; j++) 
16             w[i]+=abs(a[path[j]][i]-a[path[j+1]][i]); //记录已选择的行之间第i列的绝对值
17     for(int i=1; i<=m; i++) 
18         for(int j=i+1; j<=m; j++) 
19             for(int k=1; k<=r; k++) 
20                 v[i][j]+=abs(a[path[k]][i]-a[path[k]][j]); //预处理每列之间的绝对值差
21     f[0][0]=0; 
22     for(int i=1; i<=c; i++) 
23         for(int j=i; j<=m; j++) 
24             for(int k=0; k<j; k++) 
25                 f[i][j]=min(f[i][j], f[i-1][k]+w[j]+v[k][j]);//dp
26     for(int i=c; i<=m; i++) ans=min(ans, f[c][i]); //寻找答案
27 
28 }
29 void dfs(int now, int pre){
30     if(now>r){
31         work();
32         return ;
33     }
34     for(int i=pre+1; n-i>=r-now; i++){
35         path[now]=i;
36         dfs(now+1, i);
37     }
38 }
39 int main(){
40     scanf("%d%d%d%d",&n,&m,&r,&c);
41     for(int i=1; i<=n; ++i)
42         for(int j=1; j<=m; ++j)
43             scanf("%d",&a[i][j]);
44     dfs(1, 0);
45     cout<<ans<<endl;
46     return 0;
47 }

猜你喜欢

转载自www.cnblogs.com/Aze-qwq/p/9879711.html