题意:
一个n * n的棋盘上放着n个棋子,现在要求把这n个棋子用最少的步数移到同一条直线上,即同一列同一行或者同一对角线(两条)。输出最少的步数(只能往四个方向移动,即正东,正西,正南,正北)。
思路:
每个棋子唯一对应一个格子,每个棋子不能在同一个格子,那么就相当于一个二分图(强行二分图)。
因为n很小,所以可以枚举每一行,每一列,两条对角线,然后每个点移动到每一条直线的每一个格子都有一个距离,那么这个点就向格子连一条权值为距离的边,这个问题就转化成了求所有最佳完美的匹配中的最小值,用KM算法。
因为我们求的是最小步数,所以求的是带权二分图的最小匹配。
求最小匹配,就将每一条边的权值取反,然后求最大匹配,再将最后的结果取反就得到了最小匹配的结果。
复杂度为O(n^4)。
注意:输出很坑,每一个答案后都有一个换行,并不是两个中间输出一个,也就是说,最后一个后面也有换行。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <vector> 5 using namespace std; 6 7 const int N = 20; 8 const int inf = 0x3f3f3f3f; 9 10 int love[N][N]; 11 int lx[N],ly[N]; 12 bool visx[N],visy[N]; 13 int match[N]; 14 int slack[N]; 15 16 struct node 17 { 18 int x,y; 19 20 node(int aa,int bb) 21 { 22 x = aa; 23 y = bb; 24 } 25 }; 26 27 vector<node> vn; 28 29 int mabs(int x) 30 { 31 return x >= 0 ? x : -x; 32 } 33 34 bool dfs(int u,int n) 35 { 36 visx[u] = 1; 37 38 for (int i = 1;i <= n;i++) 39 { 40 if (visy[i]) continue; 41 42 int gap = lx[u] + ly[i] - love[u][i]; 43 44 if (gap == 0) 45 { 46 visy[i] = 1; 47 48 if (match[i] == -1 || dfs(match[i],n)) 49 { 50 match[i] = u; 51 return true; 52 } 53 } 54 else 55 { 56 slack[i] = min(gap,slack[i]); 57 } 58 } 59 60 return false; 61 } 62 63 int km(int n) 64 { 65 memset(match,-1,sizeof(match)); 66 memset(ly,0,sizeof(ly)); 67 68 for (int i = 1;i <= n;i++) 69 { 70 lx[i] = love[i][1]; 71 72 for (int j = 2;j <= n;j++) 73 { 74 lx[i] = max(lx[i],love[i][j]); 75 } 76 } 77 78 for (int i = 1;i <= n;i++) 79 { 80 memset(slack,inf,sizeof(slack)); 81 82 while (1) 83 { 84 memset(visx,0,sizeof(visx)); 85 memset(visy,0,sizeof(visy)); 86 87 if (dfs(i,n)) break; 88 89 int d = inf; 90 91 for (int j = 1;j <= n;j++) 92 { 93 if (!visy[j]) d = min(d,slack[j]); 94 } 95 96 for (int j = 1;j <= n;j++) 97 { 98 if (visx[j]) lx[j] -= d; 99 100 if (visy[j]) ly[j] += d; 101 } 102 } 103 104 } 105 106 107 int res = 0; 108 109 for (int i = 1;i <= n;i++) 110 { 111 res += love[match[i]][i]; 112 } 113 114 return res; 115 } 116 117 int main() 118 { 119 int n; 120 int kase = 0; 121 122 while (scanf("%d",&n) != EOF && n) 123 { 124 vn.clear(); 125 126 for (int i = 0;i < n;i++) 127 { 128 int a,b; 129 130 scanf("%d%d",&a,&b); 131 132 vn.push_back(node(a,b)); 133 } 134 135 //if (kase) printf(" "); 136 137 int ans = 1e8; 138 139 for (int i = 1;i <= n;i++) 140 { 141 for (int j = 0;j < vn.size();j++) 142 { 143 for (int k = 1;k <= n;k++) 144 { 145 int dx = mabs(vn[j].x - i); 146 int dy = mabs(vn[j].y - k); 147 love[j+1][k] = -(dx + dy); 148 } 149 } 150 151 int tmp = -km(n); 152 153 //printf("%d ** ",tmp); 154 155 ans = min(tmp,ans); 156 } 157 158 for (int i = 1;i <= n;i++) 159 { 160 for (int j = 0;j < vn.size();j++) 161 { 162 for (int k = 1;k <= n;k++) 163 { 164 int dy = mabs(vn[j].y - i); 165 int dx = mabs(vn[j].x - k); 166 love[j+1][k] = -(dx + dy); 167 } 168 } 169 170 int tmp = -km(n); 171 172 ans = min(tmp,ans); 173 174 //printf("%d ** ",tmp); 175 } 176 177 for (int i = 1;i <= n;i++) 178 { 179 for (int j = 0;j < vn.size();j++) 180 { 181 int dx = mabs(vn[j].x - i); 182 int dy = mabs(vn[j].y - i); 183 184 love[j+1][i] = -(dx + dy); 185 } 186 } 187 188 ans = min(ans,-km(n)); 189 190 for (int i = 1;i <= n;i++) 191 { 192 for (int j = 0;j < vn.size();j++) 193 { 194 int dx = mabs(vn[j].x - i); 195 int dy = mabs(vn[j].y - (n - i + 1)); 196 197 love[j+1][i] = -(dx + dy); 198 } 199 } 200 201 ans = min(ans,-km(n)); 202 203 printf("Board %d: %d moves required. ",++kase,ans); 204 } 205 206 return 0; 207 }