练习题 James and Dominoes
英文题意描述:
James has a grid plate of size N×N. And non-negative integers are written on each grid unit of the plate.
As James hates when there are too many numbers, he wants to cover a part of the plate with dominoes.
The size of a domino tile is 1×2. You can cover the numbers by putting the dominoes in a way that they overlap with exactly 2 grid units (horizontally or vertically).
However, a grid unit cannot be overlapped with several dominoes.
When you cover the grid plate with dominoes under this condition, your job is to minimize the sum of the numbers you can see.
[Input]
The first line contains a single integer Tㅡthe number of total test cases.
The first line of each test case contains the size of the grid plate, N(1 ≤ N ≤ 2000) and the number of dominoes, K(1 ≤ K ≤ 5).
The next N lines contain N integers that are greater than or equal to 0 and less than or equal to 1000. These integers mean the numbers written on the grid plate.
[Output]
Each line begins with ‘#x’(Test case number) followed by a space and then print the minimum value among the possible sums of numbers that are seen after covering the grid with dominoes.
Input Example
2 // Number of test case
3 1 // First test case, N=3, K=1
2 7 6 // Grid plate of size 3×3
9 5 1
4 3 8
4 2 // Second test case
1 2 4 0
4 0 5 4
0 3 5 1
1 0 4 1
Output Example
#1 31 // Answer to the first test case
#2 17
题意:N*N非负数字(最大1000)矩阵(最大边长2000),给1*2的多米诺骨牌(最多5个),求被多米诺骨牌遮挡之后的数字之和最小是多少。
思路:DFS,剪枝效仿贪心算法,只从前7 * (k - 1) + 1大的组合里寻找解决方案。
用domino去尽量大的覆盖掉原矩阵。直接暴力递归的话,层数太多了,且不说内存问题,时间上也不允许。但是可以知道,一些情况是不需要递归的。比如,矩阵最小的一个1*2区域。再怎么选也肯定轮不到选它。实际上肯定不是只有这一个最小的区域轮不到选它。
当这个节点小到,有7*(k-1)+1个值比它大的时候就轮不到它了。也就是说,实际上我们做递归时,只需要在7*(k-1)+1这么多个值中递归就行了。
举个例子,我们要选2个区域出来,选取第一个区域后,会有6个其他的1*2区域会和它重合,算上它自己,一个7个区域处于不可以选择的状态。假如这7个值,正好是最大的7个值。那么我们选第二个区域时,就只能选第8大的值。假如最大的7个值不都和第一个区域重合,那么我们就不需要第8个值。也就是说最坏的情况下。选2个区域,只需要在8个最大值中递归就行了。
1 #include <stdio.h> 2 typedef struct domino{ 3 int x1, x2, y1, y2, v; 4 bool operator>(const domino &d){ 5 return this->v > d.v; 6 } 7 }; 8 domino d[50]; 9 int tc, n, k, dn, sub, sum; 10 int data[2001][2001]; 11 bool b[2001][2001]; 12 void push(int x1, int x2, int y1, int y2, int v){ 13 d[dn] = { x1, x2, y1, y2, v }; 14 for (int i = dn; i > 0; i--){ 15 if (d[i] > d[i - 1]){ 16 domino tmp = d[i]; 17 d[i] = d[i - 1]; 18 d[i - 1] = tmp; 19 } 20 else break; 21 } 22 } 23 void dfs(int deep, int loc, int tsum){ 24 if (deep == k){ 25 if (sub < tsum)sub = tsum; 26 return; 27 } 28 for (int i = loc; i < dn; i++){ 29 if (!b[d[i].y1][d[i].x1] && !b[d[i].y2][d[i].x2]){ 30 b[d[i].y1][d[i].x1] = b[d[i].y2][d[i].x2] = 1; 31 dfs(deep + 1, i + 1, tsum + d[i].v); 32 b[d[i].y1][d[i].x1] = b[d[i].y2][d[i].x2] = 0; 33 } 34 } 35 } 36 int main(){ 37 // freopen("sample_input.txt", "r", stdin); 38 scanf("%d", &tc); 39 for (int t = 1; t <= tc; t++){ 40 scanf("%d%d", &n, &k); 41 sub = 0, sum = 0, dn = 7 * (k - 1) + 1; 42 for (int i = 0; i < dn; i++)d[i] = { 0, 0, 0, 0, 0 }; 43 for (int i = 0; i < n; i++) 44 for (int j = 0; j < n; j++){ 45 scanf("%d", &data[i][j]); 46 sum += data[i][j]; 47 } 48 for (int i = 0; i < n; i++) 49 for (int j = 0; j < n; j++){ 50 if (j + 1 < n)push(i, i, j, j + 1, data[i][j] + data[i][j + 1]); 51 if (i + 1 < n)push(i, i + 1, j, j, data[i][j] + data[i + 1][j]); 52 } 53 dfs(0, 0, 0); 54 printf("#%d %d\n", t, sum - sub); 55 } 56 return 0; 57 }