• 【洛谷P2258】子矩阵【DFS+DP】


    题目描述

    给出如下定义:

    1. 子矩阵: 从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序) 被称为原矩阵的一个子矩阵。例如,下面左图中选取第 2、 4 行和第 2、 4、 5 列交叉位置的元素得到一个 2*3 的子矩阵如右图所示。

    在这里插入图片描述

    1. 相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的。
    2. 矩阵的分值: 矩阵中每一对相邻元素之差的绝对值之和。

    本题任务:给定一个 n 行 m 列的正整数矩阵,请你从这个矩阵中选出一个 r 行 c 列的子矩阵,使得这个子矩阵的分值最小,并输出这个分值。

    输入

    第一行包含用空格隔开的四个整数 n,m,r,c,意义如问题中所述,每两个整数之间用一个空格隔开。
    接下来的 n 行, 每行包含 m 个用空格隔开的整数,用来表示问题中那个 n 行 m 列的矩阵。

    输出

    输出共 1 行,包含 1 个整数,表示满足题目描述的子矩阵的最小分值。

    数据范围限制

    对于 100%的数据, 1 ≤m ≤ 16 , 1 ≤ n≤ 16 , 矩阵中的每个元素 1 ≤ a [i , j] ≤1000,1 ≤ r<=n ,1<=c<=m .

    分析

    两年之后做这道题,依然收获很多。

    之前的思路:
    边搜索边dp枚举选那些行 然后算出每列之间的分数w 两列之间的分数v
    就可以开始dp了 f[i][j]表示已经选了i列 最后一列是j的最小分数

    但是这次有点不太一样的理解,由于子矩阵的定义,直接枚举肯定超时,所以就考虑行和列枚举其中一个,然后另一个跑DP。具体怎么枚举呢,这里的枚举用搜索实现,可以得出所有行或者列的组合,这次我就枚举了列的组合。然后知道了列,就可以处理处横着合并(每一行)的分值,然后再求出任意两行合并的分值,就可以开始DP了。

    f [ i ] [ j ] f[i][j] f[i][j]表示已经选了 i i i 行 最后一行是 j j j 的最小分数,枚举一个k,表示上一行选的是第k行,k的范围显然是 [ i − 1 , j − 1 ] [i-1,j-1] [i1,j1] ,所以有转移方程: f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i − 1 ] [ k ] + h b [ k ] [ j ] + l i n e [ j ] ) ; f[i][j]=min(f[i][j],f[i-1][k]+hb[k][j]+line[j]); f[i][j]=min(f[i][j],f[i1][k]+hb[k][j]+line[j]); 表示要加上行之间合并的分值和该行的分值。

    不难发现,枚举列,按行DP,枚举行,按列DP都可以得出答案,恰好就是我之前和现在两种不同的做法。

    上代码

    code#1:

    #include
    #include
    #include
    #include
    using namespace std;
    
    int m,n,ans=2147483647,x,y,r,c,ff[101][101],f[101],a[101][101],v[101][101],b[101];
    
    void dp()
    {
    	memset(ff,0,sizeof(ff));
    	memset(v,0,sizeof(v));
    	memset(f,0,sizeof(f));
    	b[r+1]=b[r];
    	for(int i=1;i<=m;i++)
    	{
    		for(int j=1;j<=r;j++)
    		{
    			f[i]+=abs(a[b[j]][i]-a[b[j+1]][i]);//处理列 
    		}
    	}
    	for(int i=1;i<=m;i++)
    	{
    		for(int j=i+1;j<=m;j++)
    		{
    			for(int k=1;k<=r;k++)
    			{
    				v[i][j]+=abs(a[b[k]][i]-a[b[k]][j]);
    			}
    		}
    	} 
    	for(int i=1;i<=m;i++)
    	{
    		ff[1][i]=f[i];
    	}
    	for(int i=2;i<=c;i++)
    	{
    		for(int j=i;j<=m;j++)
    		{
    			int t=2147483647;
    			for(int k=i-1;k<=j-1;k++)
    			{
    				t=min(t,ff[i-1][k]+v[k][j]);
    			}
    			ff[i][j]=t+f[j];
    		}
    	}
    	for(int i=c;i<=m;i++)
    	{
    		ans=min(ans,ff[c][i]);
    	}
    } 
    void dfs(int x)
    {
    	if(y==r)
    	{
    		dp();
    		return;
    	}
    	for(int i=x+1;i<=n;i++)
    	{
    		y++;
    		b[y]=i;
    		dfs(i);//递归 
    		y--;
    	}
    }
    int main()
    {
        cin>>n>>m>>r>>c;
        for(int i=1;i<=n;i++)
        {
        	for(int j=1;j<=m;j++)
        	{
        		cin>>a[i][j];
    		}
    	}
    	dfs(0);
    	cout<<ans;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    code #2(new):

    #include
    #include
    #include
    #include
    #include
    using namespace std;
    
    int n,m,r,c,y,a[20][20],f[20][20],ans=0x3f3f3f3f;
    int col[20],line[20],hb[20][20];
    
    int DP()
    {
    	memset(line,0,sizeof(line));
    	memset(hb,0,sizeof(hb));
    	memset(f,0x3f,sizeof(f));
    	/*---------处理每行的差值------------*/
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=y-1;j++)
    			line[i]+=abs(a[i][col[j]]-a[i][col[j+1]]);
    	/*--------处理合并每行的代价---------*/ 
    	for(int i=1;i<=n-1;i++)
    		for(int j=i+1;j<=n;j++)
    			for(int k=1;k<=c;k++)
    				hb[i][j]+=abs(a[i][col[k]]-a[j][col[k]]);
    	/*-------DP转移,k是上一行在哪-------*/			
    	for(int i=1;i<=n;i++) f[1][i]=line[i];
    	for(int i=2;i<=r;i++)
    		for(int j=i;j<=n;j++)
    			for(int k=i-1;k<=j-1;k++)
    				f[i][j]=min(f[i][j],f[i-1][k]+hb[k][j]+line[j]);
    				
    	int s=0x3f3f3f3f;
    	for(int i=r;i<=n;i++) s=min(s,f[r][i]);
    	return s;
    }
    
    void dfs(int x)
    {
    	if(y==c)
    	{
    		ans=min(ans,DP());
    		return;
    	}
    	for(int i=x+1;i<=m;i++)
    	{
    		y++;
    		col[y]=i;
    		dfs(i);
    		y--;
    	}
    }
    
    int main()
    {
    	cin>>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);
    	cout<<ans;
    	return 0;
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 相关阅读:
    SPA项目开发之动态树+数据表格+分页
    小白学爬虫:手机app分享商品短连接获取淘宝商品链接接口|淘宝淘口令接口|淘宝真实商品链接接口|淘宝商品详情接口
    看了两位阿里P10的成长经历,我的认知升华了
    工业数据与数据采集应用如何在ARM+FPGA异核架构的米尔MYC-JX8MMA7核心板应用
    echoServer回显服务器
    【附源码】Python计算机毕业设计某服装店购物网站
    痞子衡嵌入式:简析i.MXRT1170 MECC64功能特点及其保护片内OCRAM1,2之道
    ceph 笔记
    Kafka RecordAccumulator 三 高并发写入数据
    FPGA/SoC控制机械臂
  • 原文地址:https://blog.csdn.net/dglyr/article/details/126452755