求m*n的01矩阵的r*c子式

liaomingxue none 服务器端工程师  2010-05-27 12:31:51
求m*n的01矩阵的r*c子式

要求有三点:
1)子式为全1矩阵
2)r>=G(G为一正常数)
3)c值越大越好,c值相同情况下r值越大越好
...全文
263 点赞 收藏 12
写回复
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
liaomingxue 2010-06-30
经深入研究,此问题属于最大二分团极值问题,即求出所有最大二分团,然后从中筛选出其中的最大者(最大的最大二分团)

故此问题可暂且停止,若继续研究,则研究最大二分团搜索问题。

呵呵
回复
zyl072 2010-06-29
[Quote=引用 10 楼 liaomingxue 的回复:]
楼上的结果不符合原题要求:

下面的测试数据中,存在三行(0,1,2行)两列(1,3列)构成的子矩阵满足r>=3且c>1即比测试结果好
注意子矩阵的定义:是从原来矩阵中选择任意x行任意y列构成的矩阵,所选的行或列不需要保持连续性

测试数据:
4 5 3
1 1 0 1 0
1 1 1 1 0
0 1 1 1 1
1 0 0 1 0
输出:
x=0, y=3, r=4, c……
[/Quote]

如果是这样的话,那应该是个NP难问题了。不知道数据范围大不大。
我能给出 O(N * 2^N)的算法。

首先将每一行按二进制位转换成整数。 例如:0 1 0 1 1 0 直接转换成 010110,即:22。
然后枚举这N行的所有元素个数不小于G个的组合。对于每一个组合,将他们转换之后的整数进行求操作。这样就可以得到他们共同为1的位。始终记录一个1最多的最优解(即C最大),倘若1的位数相同时,则记录元素个数最多的组合(即在C最大的情况下,R最大)。
这样最后就可以得到最优解。
组合中的元素即代表所选择的行,他们求与操作结果为1的位,即表示所选择的列。
回复
liaomingxue 2010-06-29
楼上的结果不符合原题要求:

下面的测试数据中,存在三行(0,1,2行)两列(1,3列)构成的子矩阵满足r>=3且c>1即比测试结果好
注意子矩阵的定义:是从原来矩阵中选择任意x行任意y列构成的矩阵,所选的行或列不需要保持连续性

测试数据:
4 5 3
1 1 0 1 0
1 1 1 1 0
0 1 1 1 1
1 0 0 1 0
输出:
x=0, y=3, r=4, c=1
回复
zyl072 2010-06-28
自己琢磨了一下,想出了O(N*M)的算法。
算法思路:

需要用到4个矩阵。
A矩阵:记录原01矩阵

B矩阵:记录该元素往上最多有几个连续的1。
B矩阵计算方法:假如a[i][j] = 0,则b[i][j] = 0。否则,b[i][j] = b[i-1][j]+1。

C矩阵:记录从该元素开始往左的满足r>=g的最大列数C。即题目中的C越大越好的条件。
C矩阵计算方法:假如b[i][j]<g,说明该列不满足条件,即c[i][j]=0。否则,满足条件的最大列数c[i][j] = c[i][j-1] + 1。

D矩阵:C矩阵的补充说明,记录在满足C最大的情况中,R可达的最大高度。
D矩阵的计算方法:假如b[i][j]<g,说明该列不满足条件,此时R无意义,d[i][j]设为无穷大。否则,d[i][j] = min{b[i][j], d[i][j-1]}。

O(N*M)的代码,应该不会太难理解:
#include <stdio.h>

#define MAXN 1010
#define MAXINT 0x7FFFFFFF

int n, m, g;

int a[MAXN][MAXN], b[MAXN][MAXN], c[MAXN][MAXN], d[MAXN][MAXN];

int bestc, bestr, bestx, besty;

int main(){
int i, j;
while (scanf("%d %d %d", &n, &m, &g)!=EOF){
bestc = 0;
for (i=0; i<n; ++i)
for (j=0; j<m; ++j){
scanf("%d", &a[i][j]);
if (a[i][j]) {
b[i][j] = i ? b[i-1][j]+1 : 1;
} else {
b[i][j] = 0;
}

if (b[i][j] >= g){
c[i][j] = 1;
d[i][j] = b[i][j];
if (j) {
c[i][j] += c[i][j-1];
if (d[i][j-1] < d[i][j])
d[i][j] = d[i][j-1];
}
if (c[i][j] > bestc || c[i][j] == bestc && d[i][j] > bestr){
bestc = c[i][j];
bestr = d[i][j];
bestx = i - bestr + 1;
besty = j - bestc + 1;
}
} else {
c[i][j] = 0;
d[i][j] = MAXINT;
}
}

if (bestc == 0)
puts("impossible");
else
printf("x=%d, y=%d, r=%d, c=%d\n", bestx, besty, bestr, bestc);

}
}



测试(注意行和列均从0开始计):

测试数据:
4 5 2
1 1 0 1 0
1 1 1 1 0
0 1 1 1 1
1 0 0 1 0
输出:
x=1, y=1, r=2, c=3

测试数据:
4 5 3
1 1 0 1 0
1 1 1 1 0
0 1 1 1 1
1 0 0 1 0
输出:
x=0, y=3, r=4, c=1
回复
zyl072 2010-06-28
[Quote=引用 5 楼 mysword 的回复:]
O(m*n)
[/Quote]
您这跟没说有什么区别?能否讲具体?
回复
绿色夹克衫 2010-06-24
顶一下mysword牛的答案,这个问题用极大化的方法,可以到O(m*n)。
回复
liaomingxue 2010-06-23
经深入研究,此问题属于最大二分团极值问题,即求出所有最大二分团,然后从中筛选出其中的最大者(最大的最大二分团)
回复
gnefuil 2010-06-01
O(m*n)
回复
budweiser 2010-06-01
To : superdullwolf


你说的 蒙版匹配 我已经理解了 多谢了, 非常感激
回复
超级大笨狼 2010-06-01
To:budTang

01矩阵,比如第一行01111000011转换成int是963,第二行是01110001111=911
要想知道是否在第一行从左边第2个位置开始有4个连续的1,就要构造出一个mask蒙板01111000000=960
去和他进行“与”运算,如果结果还是蒙板960,那就是真。

构造蒙板的办法在二进制运算中很重要。
一般从右构造,比如1<<5=10000
再减个1,就是1111
再平移6个,就是1111000000
这一切都是O(1)的。

那问题回到楼主的问题上来。
如果他想知道是否存在2×3的1矩阵,那么用模板去匹配的话,就可以用带有111特点的数字去与运算。

总体复杂度要比其他方式小。
假设是m*n矩阵,(m<32,n<32,假设n是行,m是列)
第一步,把n行转成n个int,每行按位,移,与运算,是O(m*n)
第二步,构造蒙板,r*c子阵,就是有r*(m-c)个数字,这些可以当做常数,也可以理解为O(r*(m-c))
第三步,蒙板依次和n行做与运算,r×(m-c)×n次运算之内,就可以知道了。

所以总的复杂度是
O(m*n)+O(r*(m-c))+O(r*(m-c)*n)
小于O(r*m*n)

回复
budweiser 2010-05-31
办法二:

还可以把二维转化成一维的,变成一维整数数组,进行二进制运算,可以迅速匹配出全是1的模式。

1 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)
2 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)


这个方法没有看懂 望详细点解答
回复
超级大笨狼 2010-05-27
办法一:

递归,深搜,就可以解决。

办法二:

还可以把二维转化成一维的,变成一维整数数组,进行二进制运算,可以迅速匹配出全是1的模式。

1 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)
2 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)

见位运算应用口诀和实例
http://blog.csdn.net/superdullwolf/archive/2009/10/10/4649080.aspx


回复
发动态
发帖子
数据结构与算法
创建于2007-08-27

3.2w+

社区成员

数据结构与算法相关内容讨论专区
申请成为版主
社区公告
暂无公告