【POJ - 3723 】Conscription(最小生成树)

Conscription

Descriptions

需要征募女兵N人,男兵M人。 每招募一个人需要花费10000美元。 如果已经招募的人中有一些关系亲密的人,那么可以少花一些钱。 给出若干男女之前的1 ~ 9999 之间的亲密度关系, 招募某个人的费用是 10000 - (已经招募了的人中和自己的亲密度的最大值)。 要求通过适当的招募顺序使得招募所有人所花费的费用最小。

Input

输入N, M, R;
接下来输入R行 (x, y, d) 表示第 x 号男兵和第 y 号女兵之间的亲密度是 d

Output

输入最小花费的值。

Sample Input

2

5 5 8
4 3 6831
1 3 4583
0 0 6592
0 1 3063
3 3 4975
1 3 2049
4 2 2104
2 2 781

5 5 10
2 4 9820
3 2 6236
3 1 8864
2 4 8326
2 0 5156
2 0 1463
4 1 2439
0 4 4373
3 4 8889
2 4 3133

Sample Output

71071
54223

题目链接

https://vjudge.net/problem/POJ-3723

把人看成点,关系看作边,转化为求解无向图的最大权森林问题,这个问题又可以通过把所有边取反之后用最小生成树的算法求解

典型的kruskal算法

注意要用scanf printf

AC代码

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include <string>
#include 
#include 
#include 
#include <set>
#include 
#define IOS                       \
    ios_base::sync_with_stdio(0); \
    cin.tie(0);
#define Mod 1000000007
#define eps 1e-6
#define ll long long
#define INF 0x3f3f3f3f
#define MEM(x, y) memset(x, y, sizeof(x))
#define Maxn 1000000 + 10
using namespace std;
int N, M, R;
int x[Maxn], y[Maxn], d[Maxn];//男孩 女孩 关系度
struct edge
{
    int u, v, cost;//u到v的距离为cost
};
bool cmp(const edge &e1, const edge &e2)//从小到大排序
{
    return e1.cost < e2.cost;
}
edge es[Maxn];
int par[Maxn];
void init(int n)//初始化并查集
{
    for (int i = 0; i <= n; i++)
        par[i] = i;
}
int findr(int x)//寻根
{
    if (par[x] == x)
        return x;
    return par[x] = findr(par[x]);
}
void unite(int x, int y)//合并
{
    x = findr(x);
    y = findr(y);
    if (x == y)
        return;
    par[x] = y;
}
bool same(int x, int y)//判断根是否相同
{
    return findr(x) == findr(y);
}
int kruskal(int V, int E)//kruskal算法求最小生成树
{
    sort(es, es + E, cmp);
    init(V);
    int res = 0;
    for (int i = 0; i < E; i++)
    {
        edge e = es[i];
        if (!same(e.u, e.v))
        {
            unite(e.u, e.v);
            res += e.cost;
        }
    }
    return res;
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)//T组测试样例
    {
        scanf("%d%d%d", &N, &M, &R);//存数据
        for (int i = 0; i < R; i++)
            scanf("%d%d%d", &x[i], &y[i], &d[i]);
        int V, E;
        V = N + M;//顶点个数
        E = R;//边个数
        for (int i = 0; i < E; i++)//存入结构体
        {
            es[i] = (edge){
                x[i], N + y[i], -d[i]};
        }
        printf("%d\n", 10000 * (N + M) + kruskal(V, E));
    }
    return 0;
}

 

 

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define  IOS                        \
     ios_base:: sync_with_stdio( 0);  \
     cin. tie( 0);
#define  Mod  1000000007
#define  eps  1e - 6
#define  ll  long  long
#define  INF  0x3f3f3f3f
#define  MEM (x, y)  memset(x, y,  sizeof(x))
#define  Maxn  1000000  +  10
using  namespace  std;
int N, M, R;
int  x[Maxn],  y[Maxn],  d[Maxn]; //男孩 女孩 关系度
struct  edge
{
     int u, v, cost; //u到v的距离为cost
};
bool  cmp( const edge  & e1const edge  & e2) //从小到大排序
{
     return  e1. cost  <  e2. cost;
}
edge  es[Maxn];
int  par[Maxn];
void  init( int  n) //初始化并查集
{
     for ( int i  =  0; i  <= n; i ++)
         par[i]  = i;
}
int  findr( int  x) //寻根
{
     if ( par[x]  == x)
         return x;
     return  par[x]  =  findr( par[x]);
}
void  unite( int  xint  y) //合并
{
    x  =  findr(x);
    y  =  findr(y);
     if (x  == y)
         return;
     par[x]  = y;
}
bool  same( int  xint  y) //判断根是否相同
{
     return  findr(x)  ==  findr(y);
}
int  kruskal( int  Vint  E) //kruskal算法求最小生成树
{
     sort(es, es  + E, cmp);
     init(V);
     int res  =  0;
     for ( int i  =  0; i  < E; i ++)
    {
        edge e  =  es[i];
         if ( ! same( e. ue. v))
        {
             unite( e. ue. v);
            res  +=  e. cost;
        }
    }
     return res;
}
int  main()
{
     int T;
     scanf( " %d "&T);
     while (T --) //T组测试样例
    {
         scanf( " %d%d%d "&N,  &M,  &R); //存数据
         for ( int i  =  0; i  < R; i ++)
             scanf( " %d%d%d "& x[i],  & y[i],  & d[i]);
         int V, E;
        V  = N  + M; //顶点个数
        E  = R; //边个数
         for ( int i  =  0; i  < E; i ++) //存入结构体
        {
             es[i]  = (edge){
                 x[i], N  +  y[i],  - d[i]};
        }
         printf( " %d \n "10000  * (N  + M)  +  kruskal(V, E));
    }
     return  0;
}

你可能感兴趣的:(【POJ - 3723 】Conscription(最小生成树))