编程之美资格赛3:格格取数

描述

给你一个m x n (1 <= m, n <= 100)的矩阵A (0<=aij<=10000),要求在矩阵中选择一些数,要求每一行,每一列都至少选到了一个数,使得选出的数的和尽量的小。

输入

多组测试数据。首先是数据组数T

对于每组测试数据,第1行是两个正整数m, n,分别表示矩阵的行数和列数。

接下来的m行,每行n个整数,之间用一个空格分隔,表示矩阵A的元素。

输出

每组数据输出一行,表示选出的数的和的最小值。

数据范围

小数据:1 <= m, n <= 5

大数据:1 <= m, n <= 100

思路:
先假设M=N,如果暴力法搜索,则需要M!次,当M=100时,根本不行。

贪心算法?先找到最小的,再找次小的,依次寻找,如果行或者列重复了就跳过继续寻找,这种方法复杂度极低,但是无法证明贪心是错的。

比如下面的例子:

0 1 4

1 5 8

3 2 4 

如果用贪心算法答案是,0+2+8=10,但这题答案明显是1+1+4=6.

之前有人用贪心算法提交结果是AC,这说明微软的测试数据有问题。

下面说说我的思路,大家来评价下是否正确:

我用动态规划,dp[k][i][j]表示横着数第i个,纵着数第j个k*k大小的矩阵的最小值。这样k从1到M,i和j是内层循环,范围是M*N矩阵内又多少个k*k的矩阵。下面举个简单的例子说明。M=N=3.

k=1时,dp[i][i][j]就是矩阵的各个元素。

k=2时,i和j的范围是2*2,先求dp[2][1][1],就是最左上角的2*2的矩形的值,他是对角线元素相加,取最小值,剩下四个2*2的矩形的值类似计算,这样k=2的都计算完了。

k=3时,i和j都只能为1了,这时的最小值是(左上角2*2矩形+右下角矩阵值),(右上角+左下角)。。。四个值的最小值。

如果k继续增加,对于每个i,j,取四个值的最小值就行,k就逐个递增。

代码如下:

//source here
#include<iostream>
#include <memory.h>
using namespace std;
int T,M,N,a[100][100],i,j,k,p;
int dp[100][100][100];
int main()
{
    cin>>T;
    for( p=0;p<T;p++)
    {
	cin>>M>>N;
	memset(dp,0,sizeof(dp));
	for(j=0;j<M;j++)
		for(k=0;k<N;k++)
		   cin>>a[j][k];		
        for(j=0;j<M;j++)
		for(k=0;k<N;k++)
		   dp[1][j+1][k+1]=a[j][k];
 		for (k=2;k<=N;k++)
			for(i=1;i<=M-k+1;i++)
				for(j=1;j<=N-k+1;j++)
				dp[k][i][j]=min(min(dp[k-1][i][j]+a[i+k-2][j+k-2],dp[k-1][i+1][j]+a[i-1][j+k-2]),min(dp[k-1][i][j+1]+a[i+k-2][j-1],dp[k-1][i+1][j+1]+a[i-1][j-1]));
    cout<<"Case "<<p+1<<":"<<dp[N][1][1]<<endl;
    }
    return 0;
}
我在VC上测的很多数据都是对的,但是提交是WA,所以我也不确定这思路是否正确,还请大神评价。

复杂度大概是4*(1^2+2^2+...+M^2),O(M^3),还可以接受。

当然题目没说M=N,当两者不等时,M>N,先计算出N*N的数,有M-N+1个,剩下M-N行每行取最小值,把前面M-N+1个最小值一次和剩下的行中的最小值相加,最后取这M-N+1个数的最小值即可。
修正:这个思路还是不对,正如下面的评论,听说可以用匈牙利法,有点复杂。。。

你可能感兴趣的:(算法,动态规划,编程之美)