校招季——程式設計題目(15、16) 約瑟夫問題 最大子矩陣和
15. 約瑟夫問題(題目042)
題目:
n個人圍成一圈,從第一個開始報數,第m個將被殺掉,最後剩下一個,其餘人都將被殺掉。求最後剩下的人的序號。例如n=6,m=5,被殺掉的人的序號為5,4,6,2,3。最後剩下1號。
解答:
思路:
如果要列印整個過程,有3種方法:
1.連結串列法:產生長度為n的迴圈連結串列,然後每遍歷m次輸出當前序號,並刪除節點,直到連結串列中只有一個元素為止。
2.陣列法:建立一個輔助陣列,初始都為0,然後每遍歷m個為0的位置就輸出當前序號,並將該位置的值改為1,直到陣列大小變成1為止。
3.陣列法:建立一個輔助陣列,其中儲存編號,每刪除一個就將後面的編號前移。
這3種方法的時間複雜度都是
為了簡化問題,我們假定序號從0開始。
對於n個人的情況,第1個殺掉的人肯定是(m-1)%n號,這時剩下的人記為m,m+1,…,n-1,0,…,m-2,將它們重新記為0,1,2,…,n-2,其中的對應關係為:
X = (x’ + m) % n
其中x為n個人時的序號,x’為n-1個人時的序號,令f(n)為n個人時最後剩餘的人的序號,則f(n)和此時的f(n-1)應該對應著同一個人,且其編號有如上式的對應關係,即:
f(n) = (f(n-1) + m) % n
其初始條件為f(1)=0。返回結果時再將f(n)+1,從而變換成序號從
int Josephus(int n, int m)
{
int result = 0, i;
for (i = 2; i <= n; ++i)
result = (result + m) % i;
return result + 1;
}
16. 最大子矩陣和(題目052)
題目:
輸入一個M*N的矩陣,其中每個元素為有正有負的整數,輸出它的最大的子矩陣元素和。如果不存在矩陣和為正數的子矩陣,返回0。
解答:
方法1:
簡單的三重迴圈求每個子矩陣的矩陣和,返回最大值,時間複雜度O(m3n3)。
方法2:
定義一個大小也為m*n的新矩陣
sub_sum[i][j] = sub_sum[i-1][j] + sub_sum[i][j-1] - sub_sum[i-1][j-1] + mt[i][j];
則對任意子矩陣{a,b}~{c,d},它的矩陣和為:
sum = sub_sum[c][d] - sub_sum[c][b-1] - sub_sum[a-1][d] + sub_sum[a-1][b-1];
總的時間複雜度為O(m2n2)。
方法3:
利用最大子序列和的性質,求出[si, ei]範圍內每列的和,再從其中找出最大連續子序列的和,就是要求的結果。時間複雜度O(mn2)。
int MaxSubMatrixSum(const Matrix *mt)
{
int rows = mt->rows, cols = mt->cols ,size = cols * sizeof(int);
int *col_sum = malloc(size);
int si, ei, j, max_sum = 0;
for (si = 0; si < rows; ++si)
{
memset(col_sum, 0, size);
for (ei = si; ei < rows; ++ei)
{
for (j = 0; j < cols; ++j)
col_sum[j] += mt->data[ei][j];
int sum = MaxSum(col_sum, cols);
if (sum > max_sum)
max_sum = sum;
}
}
return max_sum;
}