BNU 39423
题目链接:
http://acm.bnu.edu.cn/v3/problem_show.php?pid=39423
题意:
给n个点,m条边,边有权值。若两点之间没有边则设边权值为0。
集合权值的定义为集合内任意一点到其余点边权值最小值之和。问怎样选取一些点,使得点构成的集合权值最小。
思路:
最大团(即最大完全子图)。
其实就是DFS暴搜,枚举哪些点在团里,若加入不合法点直接剪枝。
源码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
#define inf (1000000000)
#define gmax(a,b) ((a) > (b) ? (a) : (b))
#define gmin(a,b) ((a) < (b) ? (a) : (b))
const int MAXN = 100 + 5;
int vis[MAXN];
int gra[MAXN][MAXN];
int val[MAXN];
int n, m;
int ans;
bool check(int mark)
{
for(int i = 1 ; i <= n ; i++){
if(vis[i] == 1 && gra[i][mark] == inf)
return false;
}
return true;
}
void DFS(int mark, int cnt)
{
if(cnt > 1){
// printf("mark = %d\n", mark);
int temp = 0;
for(int i = 1 ; i <= n ; i++){
if(vis[i]){
// printf("i = %d, val[i] = %d\n", i, val[i]);
temp += val[i];
}
}
// printf("temp = %d\n", temp);
ans = gmax(ans, temp);
}
for(int i = mark + 1 ; i <= n ; i++){
if(check(i)){
int mmin = inf;
int tval[MAXN];
for(int j = 1 ; j <= mark ; j++){
if(vis[j]){
mmin = gmin(mmin, gra[j][i]);
tval[j] = val[j];
val[j] = gmin(val[j], gra[j][i]);
}
}
vis[i] = 1;
val[i] = mmin;
DFS(i, cnt + 1);
vis[i] = 0;
val[i] = inf;
for(int j = 1 ; j <= mark ; j++)
if(vis[j])
val[j] = tval[j];
}
}
}
int main()
{
while(scanf("%d%d", &n, &m) != EOF){
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++)
gra[i][j] = inf;
int u, v, w;
for(int i = 0 ; i < m ; i++){
scanf("%d%d%d", &u, &v, &w);
gra[u][v] = gmin(gra[u][v], w);
gra[v][u] = gmin(gra[v][u], w);
}
ans = 0;
memset(vis, 0, sizeof(vis));
for(int i = 1 ; i <= n ; i++)
val[i] = inf;
for(int i = 1 ; i <= n ; i++){
vis[i] = 1;
DFS(i, 1);
vis[i] = 0;
}
printf("%d\n", ans);
}
return 0;
}