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;
}
posted @ 2013-03-20 00:11  kedebug  阅读(447)  评论(0编辑  收藏  举报