给你一个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
贪心算法?先找到最小的,再找次小的,依次寻找,如果行或者列重复了就跳过继续寻找,这种方法复杂度极低,但是无法证明贪心是错的。
比如下面的例子:
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个数的最小值即可。
修正:这个思路还是不对,正如下面的评论,听说可以用匈牙利法,有点复杂。。。