POJ 2531 Network Saboteur(简单搜索技巧 + 剪枝)
题意:
网络中有n个节点,可以把这n个节点划分成两个集合subsets A和subsets B,同一个集合中的节点间进行通讯没有时间损失;
不同集合中的节点间进行通讯会有时间损失,求一种对n个结点的划分方法,使得时间的总损失最大。
思路:
1. 需要对题目稍微进行转化,降低解题的复杂度:求A B之间的最大总损失 -> 已知总损失,求 A, B 内部的最小损失的和;
2. 对于A, B分别设置了两个队列: QA, QB 分别存放 A B 集合中的节点。对于一个节点 k,可以放在 QA 中或者 QB 中,针对这个情况进行 DFS;
3. 代码中用到了 2 点剪枝:对于 Ans <= val 则直接 return, 当然先对 Ans 进行一个粗略的最小评估,有利于这个剪枝发挥效果;
第二点就是把节点 1 默认放在了 QA 中,这样又可以把时间降低一半。最终代码跑到了 16ms
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 30;
int map[MAXN][MAXN], QA[MAXN], QB[MAXN];
int N, Ans;
void dfs(int k, int val, int a, int b) {
if (Ans <= val)
return ;
int sum = 0;
for (int i = 0; i < a; i++)
sum += map[QA[i]][k];
if (k == N) {
Ans = min(Ans, sum + val);
} else {
QA[a] = k;
dfs(k + 1, sum + val, a + 1, b);
}
sum = 0;
for (int i = 0; i < b; i++)
sum += map[QB[i]][k];
if (k == N) {
Ans = min(Ans, sum + val);
} else {
QB[b] = k;
dfs(k + 1, sum + val, a, b + 1);
}
}
int main() {
int sum = 0;
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
scanf("%d", &map[i][j]);
sum += map[i][j];
}
}
int p = 0, q = 0;
for (int i = 1; i <= N/2; i++)
for (int j = 1; j <= N/2; j++)
p += map[i][j];
for (int i = N/2+1; i <= N; i++)
for (int j = N/2+1; j <= N; j++)
q += map[i][j];
Ans = (p + q) / 2;
QA[0] = 1;
dfs(2, 0, 1, 0);
printf("%d\n", sum / 2 - Ans);
return 0;
}
-------------------------------------------------------
kedebug
Department of Computer Science and Engineering,
Shanghai Jiao Tong University
E-mail: kedebug0@gmail.com
GitHub: http://github.com/kedebug
-------------------------------------------------------