P2258 子矩阵
P2258 子矩阵
题解
这题的标签足以概括这道nice题目了
就是考虑暴力枚举 ---> 先枚举行再枚举列
----->dp优化
then AC
注释加在代码里了
55'暴力代码
//55'暴力 #include<bits/stdc++.h> using namespace std; int n,m,r,c; int a[20][20]; int dph[20],dpl[20]; //dph表示子矩阵的每一行的编号,dpl表示子矩阵的每一列的编号 int ans=0x7fffffff; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } void DP() { int now=0; //当前得到一个完整的子矩阵,计算他的分数 for(int i=1;i<=r;i++) for(int j=2;j<=c;j++) now+=abs(a[dph[i]][dpl[j]]-a[dph[i]][dpl[j-1]]); for(int i=2;i<=r;i++) for(int j=1;j<=c;j++) now+=abs(a[dph[i]][dpl[j]]-a[dph[i-1]][dpl[j]]); ans=min(ans,now); } void dfs(int x,int y,int rr,int cc) //当前走到了第x行,第y列,子矩阵已经枚举了rr行,cc列 { if(cc==c+1) return DP(); //我们先枚举行,列枚举完了那么行一定也枚举完了,所以拿去跑一个ans if((x>n&&rr!=r+1)||(y>m&&cc!=c+1)) return; //不合法情况直接退出 if(rr==r+1) //行已经枚举够了 { for(int i=y;i<=m;i++) //枚举列 { dpl[cc]=i; //记录列 dfs(x,i+1,rr,cc+1); //继续往下递归 } } else { for(int i=x;i<=n;i++) //继续枚举行 { dph[rr]=i; //记录行 dfs(i+1,y,rr+1,cc); //继续往下递归 } } } int main() { 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,1,1); printf("%d\n",ans); return 0; }
100‘ AC代码
#include<bits/stdc++.h> using namespace std; int n,m,r,c; int a[20][20]; int line[20],dp[20][20]; //dp[i][j]表示前i列,已用j列得到的最小价值 int updo[20],leri[20][20]; //updo[i]表示对于i列的上下绝对值差的和,leri[i][j]表示i列和j列左右差的和 int ans=0x7fffffff; inline int read() { int ans=0; char last=' ',ch=getchar(); while(ch<'0'||ch>'9') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=ans*10+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } //转移方程 dp[i][j] = min(dp[i][j],dp[i-k][j-1]+ver[i]+del[i-k][i]}); void DP() { memset(dp,63,sizeof(dp)); memset(updo,0,sizeof(updo)); memset(leri,0,sizeof(leri)); for(int i=1;i<=m;i++) //枚举每一列i for(int j=2;j<=r;j++) updo[i]+=abs(a[line[j]][i]-a[line[j-1]][i]);//第i列的上下差值 for(int i=1;i<=m;i++) //枚举每一列 for(int j=i+1;j<=m;j++)//枚举列,联系i,找差值 for(int k=1;k<=r;k++)//枚举子矩阵每一行 leri[i][j]+=abs(a[line[k]][j]-a[line[k]][i]); for(int i=1;i<=m;i++) dp[i][1]=updo[i]; for(int i=1;i<=m;i++) //枚举每一列 for(int j=1;j<=c;j++) //子矩阵的每一列 for(int k=1;k<i&&i-k>=j-1;k++) //k一定要小于i(不然木得减了),i-k一定要大于等于j-1,因为j-1是从i-k行的当中挑出来的 dp[i][j]=min(dp[i][j],dp[i-k][j-1]+updo[i]+leri[i-k][i]); //dp取最小,比较(1),(2) //(1)dp[i][j]也就是上一轮循环到的自己 //(2)从前i-k行中选j-1列,然后再选上当前这一列 for(int i=c;i<=m;i++) //从c行后取最小值 ans=min(ans,dp[i][c]); } void dfs(int now ,int x) //子矩阵已经枚举了now行,当前走到了第x行 { if(now==r+1) return DP(); //已经枚举够行了 if(x>=n+1) return ; //不合法直接退出 for(int i=x;i<=n;i++) //继续递归子矩阵的行 { line[now]=i; dfs(now+1,i+1); } } int main() { 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); printf("%d\n",ans); return 0; }
难得一见
某谷又炸了