【并查集+dp】Team

Team

时间限制: 1 Sec  内存限制: 128 MB
提交: 124  解决: 10
[提交] [状态] [命题人:admin]

题目描述

ACM-ICPC is a interesting game. A team takes part in this game must consist of exactly (no more and no less) three players. Every year, many new members will join the FZU ACM team. How to form teams becomes a big problem.
There are 3N members this year. Each member has a power Wi. If three distinct members x, y, z form a team, the power of this team is the minimum value of Wx, Wy, Wz.
There are M pairs of relationship called best friend. If member A and member B are best friend, they must be in the same team.
We want to form N teams, and get the maximum sum of the power of these N teams. Can you help us?

 

输入

Input is given from Standard Input in the following format:
N M
W1 W2 . . . W3N
A1 B1
.
.
.
AM BM
Constraints
1≤N, M≤103
1≤Wi≤109
1≤Ai, Bi≤3N (1≤i≤M, Ai≠Bi)
All inputs are integers.

 

输出

If it’s possible to form N teams, print one integer denotes the maximum sum of the power of these N teams. Otherwise print -1.

 

样例输入

2 1
1 2 3 4 5 6
3 4

样例输出

4

题目大意:

      先输入两个整数n和m,代表有n个人,m组朋友关系,下面一行输入n个整数,代表每个人的能力值,然后输入m行关系a,b,代表a和b是好朋友,现在要将他们进行分队,每队固定为3个人,并且每队的能力值为三个人中最小的能力值,即对于队伍1:a,b,c,其队伍的能力为w[a],w[b],w[c]三者的最小值,同时也规定如果a和b是朋友关系,则a,b一定要在同一个队伍里,问组成队伍的最大能力值之和。

解题思路:

       首先确定的是朋友关系的一定须在同一个队伍里,所以可以先按照朋友关系先将当前的情况分为三种:一人一个队,两人一个队,三人一个队,因此可以通过并查集来实现,将为朋友关系的人放到同一个集合中,并将这个集合的权值设为集合中元素能力值的最小值,如果出现了集合中的元素大于3的情况,那么这种情况一定不成立,然后根据其集合人数将其分为1人,2人,3人三种情况,分别存在数组中。

      dp[i][j]代表的是已经在一人集合中选取了i个,在两人集合中选取了j个,则若要组成一个三人小队,可以分为两种情况,选三个一人队,选一个一人一个两人队,因此可以推出动态转移方程为:

dp[i+3][j]=max(dp[i+3][j],dp[i][j]+min(v[1][i],min(v[1][i+1],v[1][i+2]));

dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+min(v[1][i],v[2][j]));

假设:一人队数组长度为len1,两人队数组长度为len2,则我们可以知道若dp[len1][len2]==0,则证明此种情况不成立,组不成全为三人队,若其不为0,则用当前的dp[len1][len2]加上三人队集合的能力值即可。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <set>
#include <utility>
#include <sstream>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define inf 0x3f3f3f3f
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define lep(i,l,r) for(int i=l;i>=r;i--)
#define ms(arr) memset(arr,0,sizeof(arr))
//priority_queue<int,vector<int> ,greater<int> >q;
const int maxn = (int)1e5 + 5;
const ll mod = 1e9+7;
ll w[3100];
int f[3100];
ll num[3100];
ll dp[3100][3100];
vector<ll> v[10];
bool ju;
int find(int x)
{
	return x==f[x]?x:find(f[x]);
}
void merge(int a,int b)
{
	int fa=find(a);
	int fb=find(b);
	if(fa!=fb) {
		if(fa<fb) {
			f[fb]=fa;
			num[fa]=num[fa]+num[fb];
			w[fa]=min(w[fa],w[fb]);
			if(num[fa]>3) ju=false;
			num[fb]=0;
		}
		else {
			f[fa]=fb;
			num[fb]=num[fb]+num[fa];
			w[fb]=min(w[fa],w[fb]);
			if(num[fb]>3) ju=false;
			num[fa]=0;
		}
	}
}
int main() 
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    #endif
    //freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(0),cin.tie(0);
    int n,m;
    cin>>n>>m;
    rep(i,1,3*n) {cin>>w[i];f[i]=i;num[i]=1;}
    ju=true;
    rep(i,1,m) {
    	int a,b;
    	cin>>a>>b;
    	merge(a,b);
    }
    if(ju==false) {
    	cout<<"-1"<<endl;
    	return 0;
    }
    rep(i,1,3*n) {
    	if(num[i]==1) v[1].push_back(w[i]);
    	if(num[i]==2) v[2].push_back(w[i]);
    	if(num[i]==3) v[3].push_back(w[i]);
    }
    int len1=v[1].size();
    int len2=v[2].size();
    int len3=v[3].size();
    sort(v[1].begin(),v[1].end());
    sort(v[2].begin(),v[2].end());
    sort(v[3].begin(),v[3].end());
    v[1].push_back(0);v[1].push_back(0);v[1].push_back(0);v[1].push_back(0);
    v[2].push_back(0);v[2].push_back(0);v[2].push_back(0);v[2].push_back(0);
    for(int i=0;i<=len1;i++) {
    	for(int j=0;j<=len2;j++) {
    		dp[i+3][j]=max(dp[i+3][j],dp[i][j]+v[1][i]);
    		dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+min(v[1][i],v[2][j]));
    	}
    }
    if(dp[len1][len2]!=0) {
	    ll ans=dp[len1][len2];
	    for(int i=0;i<len3;i++) ans+=v[3][i];
	    cout<<ans<<endl;
	}
	else {
		cout<<"-1"<<endl;
	}
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值