首页 > 代码库 > C——Network Saboteur (POJ2531)

C——Network Saboteur (POJ2531)

题目:
A university network is composed of N computers. System administrators gathered information on the traffic between nodes, and carefully divided the network into two subnetworks in order to minimize traffic between parts. 
A disgruntled computer science student Vasya, after being expelled from the university, decided to have his revenge. He hacked into the university network and decided to reassign computers to maximize the traffic between two subnetworks. 
Unfortunately, he found that calculating such worst subdivision is one of those problems he, being a student, failed to solve. So he asks you, a more successful CS student, to help him. 
The traffic data are given in the form of matrix C, where Cij is the amount of data sent between ith and jth nodes (Cij = Cji, Cii = 0). The goal is to divide the network nodes into the two disjointed subsets A and B so as to maximize the sum ∑Cij (i∈A,j∈B).

Input

The first line of input contains a number of nodes N (2 <= N <= 20). The following N lines, containing N space-separated integers each, represent the traffic matrix C (0 <= Cij <= 10000). 
Output file must contain a single integer -- the maximum traffic between the subnetworks. 

Output

Output must contain a single integer -- the maximum traffic between the subnetworks.

Sample Input

3
0 50 30
50 0 40
30 40 0

Sample Output

90

解题思路:
首先这个题目的题意就比较难理解。题目大意:有n个点,每两个点之间都有一段距离,现在要把这些点分为两部分,使得一个部分的点到另一个部分所有的点的距离和最大。(学过离散的就知道这是一个完全图)比如题中的例子:就是一个K3图(3个顶点的完全图,一个三角形),设顶点为A,B,C.由题意可知:A到B的距离为30,B到C为40,C到A为50.当把A放一边,B,C放一边时,总距离为:A到B的距离(30)+A到C的距离(50)=80.当把C放一边,A,B一边时,总距离为AC+BC=90,另一种为30+40=70.所以最大的距离和为90.
看懂题目的可能都知道要用枚举来找最大距离和,但是怎么枚举呢?用for循环?肯定行不通的。这里就用”二进制枚举“来枚举所有情况并找到最大值。

二进制枚举(非递归):
用数组的值为0或1来把所有的点来分为两部分。
 1 int a[10002];
 2 memset(a,0,sizeof(a)); //清零。
 3 for (int i=0;i<n;i++) //n:枚举的次数。
 4 {
 5   a[0]++;     //第一位加一。
 6  for (int j=0;j<n;j++)
 7    if (a[j]==2)  
 8      {
 9        a[j]=0;   //当这一位等于2时,又清零
10        a[j+1]++;  //并使下一位加一。
11      }
12 }

代码可能不好理解。下面模拟过程。
开始时的数组: 00000
a[0]++;后 10000
a[0]++等于2后,a[0]=0;a[0+1]++;01000
接下来是:
11000
00100
10100
01100
......
11111
这样就把所有的情况都枚举出来了。
下一步就只要把所有枚举中0或1的所有对应值求和,并找到最大值就可以了。
全部代码如下:
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
int a[22][22],tab[22];

int main()
{
    int n,i,j,k,ans=0,sum=0,z;
    cin >>n;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
        cin >>a[i][j];
        k=pow(2,n-1);   //枚举的情况总数。

    for (i=1;i<=k;i++)
    {
        tab[1]++;
        sum=0;
        for (j=1;j<=n;j++)
            if (tab[j]==2)
        {
           tab[j]=0;
           tab[j+1]++;
        }
        else break;
        for (j=1;j<=n;j++)
        {
            if (tab[j]==0)continue;  //找到每一个为1的点。
            for (z=1;z<=n;z++)    
                if (tab[z]==0)           //并把这个1到所有0的距离求和。
                sum+=a[j][z];
        }
        ans=max(ans,sum);    //找到最大的的和。
    }
    cout <<ans<<endl;
    return 0;
}

 





C——Network Saboteur (POJ2531)