Description
Input
Output
题目大意:一棵树上有n个结点,每个结点有一个男生或者一个妹纸,每条边有一个距离,问最少交换多少个人,使得妹纸在距离D内至少有一个男生。。。
思路:换句话说,这题可以理解为:交换多少个0或1,使得每个结点在D的距离内有一个1(1的男孩纸)。
那么用DLX搜索,每一列代表一个点。每一行代表一个点,每行的结点为这个点为1可以保护的所有点(包括自己)。
然后套DLX。
加入两个剪枝:搜索到的交换若大于当前答案,则剪枝。在每一层作一个乐观估计,估计最少还需要选出多少个点,若大于点为1的点数,则剪枝。
这题正解大概为DP。我不会。
代码(1171MS):
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 using namespace std; 7 8 const int MAXN = 55; 9 const int MAXC = MAXN; 10 const int MAXR = MAXN; 11 const int MAXP = MAXR * MAXN + MAXC; 12 13 int boy[MAXN]; 14 int mat[MAXN][MAXN]; 15 int n, D, boys; 16 17 struct DLX { 18 int n, sz;//列数,结点总数 19 int sum[MAXC];//每列拥有的结点数 20 int row[MAXP], col[MAXP];//结点所在的行和列 21 int left[MAXP], right[MAXP], up[MAXP], down[MAXP];//十字链表 22 int ans, anst[MAXR]; 23 24 void init(int nn) { 25 n = nn; 26 for(int i = 0; i <= n; ++i) { 27 up[i] = down[i] = i; 28 left[i] = i - 1; right[i] = i + 1; 29 col[i] = i; 30 } 31 right[n] = 0; left[0] = n; 32 sz = n + 1; 33 memset(sum, 0, sizeof(sum)); 34 } 35 36 void add_row(int r, vector<int> &columns) { 37 int first = sz; 38 for(int i = 0, len = columns.size(); i < len; ++i) { 39 int c = columns[i]; 40 left[sz] = sz - 1; right[sz] = sz + 1; down[sz] = c; up[sz] = up[c]; 41 down[up[c]] = sz; up[c] = sz; 42 row[sz] = r; col[sz] = c; 43 ++sum[c]; ++sz; 44 } 45 right[sz - 1] = first; left[first] = sz - 1; 46 } 47 48 void remove(int c) { 49 for(int i = down[c]; i != c; i = down[i]) { 50 left[right[i]] = left[i]; 51 right[left[i]] = right[i]; 52 } 53 } 54 55 void restore(int c) { 56 for(int i = down[c]; i != c; i = down[i]) { 57 left[right[i]] = i; 58 right[left[i]] = i; 59 } 60 } 61 62 bool vis[MAXC]; 63 64 int A() { 65 memset(vis, 0, sizeof(vis)); 66 int ret = 0; 67 for(int i = right[0]; i != 0; i = right[i]) if(!vis[i]) { 68 ++ret; 69 for(int j = down[i]; j != i; j = down[j]) { 70 for(int k = right[j]; k != j; k = right[k]) vis[col[k]] = true; 71 } 72 } 73 return ret; 74 } 75 76 void dfs(int dep) { 77 if(dep + A() > boys) return ; 78 int tmp = 0; 79 for(int i = 0; i < dep; ++i) tmp += boy[anst[i]]; 80 if(dep - tmp >= ans) return ; 81 if(right[0] == 0) { 82 ans = dep - tmp; 83 return ; 84 } 85 int c = right[0]; 86 for(int i = right[0]; i != 0; i = right[i]) if(sum[i] < sum[c]) c = i; 87 for(int i = down[c]; i != c; i = down[i]) { 88 anst[dep] = row[i]; 89 remove(i); 90 for(int j = right[i]; j != i; j = right[j]) remove(j); 91 dfs(dep + 1); 92 for(int j = left[i]; j != i; j = left[j]) restore(j); 93 restore(i); 94 } 95 } 96 97 bool solve() { 98 ans = n + 1; 99 dfs(0); 100 return ans != n + 1; 101 } 102 } S; 103 104 void floyd() { 105 for(int k = 1; k <= n; ++k) 106 for(int i = 1; i <= n; ++i) if(mat[i][k] <= D) 107 for(int j = 1; j <= n; ++j) if(mat[k][j] <= D) 108 mat[i][j] = min(mat[i][j], mat[i][k] + mat[k][j]); 109 } 110 111 int main() { 112 int T; scanf("%d", &T); 113 for(int t = 1; t <= T; ++t) { 114 scanf("%d%d", &n, &D); 115 memset(mat, 0x3f, sizeof(mat)); 116 boys = 0; 117 for(int i = 1; i <= n; ++i) scanf("%d", &boy[i]), boys += boy[i]; 118 for(int i = 1; i < n; ++i) { 119 int u, v, c; 120 scanf("%d%d%d", &u, &v, &c); 121 mat[u][v] = mat[v][u] = c; 122 } 123 for(int i = 1; i <= n; ++i) mat[i][i] = 0; 124 floyd(); 125 S.init(n); 126 for(int i = 1; i <= n; ++i) { 127 vector<int> columns; 128 for(int j = 1; j <= n; ++j) if(mat[i][j] <= D) columns.push_back(j); 129 S.add_row(i, columns); 130 } 131 bool flag = S.solve(); 132 printf("Case #%d: ", t); 133 if(flag) printf("%d\n", S.ans); 134 else puts("-1"); 135 } 136 }