输入 n,m
给 3*n个人的能力值,和m 对 关系,
有关系的人必须组成一队,,(一队只能3人)
2 1
1 2 3 4 5 6
3 4
整理后的结果是
单人的 1 2 5 6 两人一组的是 (3,4) 要想组成一队,就是 1,2,5 (3,4,6) 这是一种组法
组成队伍后的能力值是最小的那个人的能力值,所有队伍能力值最大是多少?
大佬思路: 并查集处理原始数据,变成 单人双人小组分开
组成队伍的情况只有两种,1,三个单人 。2,一单一双
然后DP。(这四个字让我眼前一黑)
dp[i][j] 表示 用了 i 个单人 ,j个双人 小组来 组成的 队伍 的 能力最大值
dp[i][j] 需要考虑的是,能否组成队伍,怎么组成队伍(最弱的人分给哪两个倒霉蛋)
状态转移方程:(不知道写的对不对)
dp[i][j]=max(dp[i-3][j] + 单【i】, dp[i-1][j-1] + min(单【i】,双【j】) )
i,j 状态需要前面的状态推出来,那就从前向后递推,dp[0][0] = 0
如果 dp【i】【j】能组成队伍 dp【i+3】【j】 和 dp【i+1】【j+1】就都能组成队伍
dp【i+3】【j】=max(dp【i+3】【j】,dp【i】【j】+a【i】);
dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+min(a[i],b[j] ) )
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[3100][3100];
int f[3003];
ll dui[3003];
int sum[3003];
ll v[3003];
vector<int>q1;
vector<int>q2;
vector<int>q3;
bool flag=0;
int findd(int a)
{
if(f[a]==a)
return a;
else
return f[a]=findd(f[a]);
}
void marge(int a,int b)
{
int fa=findd(a);
int fb=findd(b);
if(fa!=fb){
// if(fa<fb){
f[fb]=fa; //合并,
sum[fa]+=sum[fb];// 把 第 fb 支队伍的人数 加入到第 fa支队伍中去
if(sum[fa]>3)
flag=1;
sum[fb]=0;
v[fa]=min(v[fa],v[fb]);// 第fa 支队伍的能力值,,,其实直接用 dui【】 这个数组就行
//
// }
// else
// {
// f[fa]=fb;
// sum[fb]+=sum[fa];
// if(sum[fb]>3)
// flag=1;
// sum[fa]=0;
// v[fb]=min(v[fa],v[fb]);
// }
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<3*n;i++)
{
cin>>dui[i]; //第i人的能力
f[i]=i; //第i人属于哪个队伍
sum[i]=1; //第i人所在队伍人数
v[i]=dui[i]; //第i人的能力
}
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;
marge(a-1,b-1);
}
if(flag==1){
cout<<-1<<endl;
return 0;
}
for(int i=0;i<3*n;i++)
{
if(sum[i]==1){
q1.push_back(v[i]);
}
if(sum[i]==2){
q2.push_back(v[i]);
}
if(sum[i]==3){
q3.push_back(v[i]);
}
}
sort(q1.begin(),q1.end());
sort(q2.begin(),q2.end());
sort(q3.begin(),q3.end());
int l1=q1.size();
int l2=q2.size();
int l3=q3.size();
q2.push_back(0);
memset(dp,-1,sizeof dp);
dp[0][0]=0;
for(int i=0;i<l1;i++)
for(int j=0;j<=l2;j++) // 这里一定要 = l2 因为 dp[i+3][j] 的j 会到 l2
{
if(dp[i][j]!=-1)
{
dp[i+3][j]=max(dp[i+3][j],dp[i][j]+q1[i]);
dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+min(q1[i],q2[j]));
}
}
long long ans=0;
if(dp[l1][l2]==-1)
{
cout<<-1<<endl;
}
else{
for(int i=0;i<l3;i++)
ans+=q3[i];
cout<<(ll)dp[l1][l2]+ans<<endl;
}
return 0;
}
问题:不会发生 一个人同时加入了,由三个单人组成的队伍和 一个单人,一个两人小组的队伍
不会的,因为你已经排序了
最优解的计算方式只有一种:
着急开班会,字迹潦草,多多包涵。
这个题,我觉得还不是特别理解,,希望有人和我讨论讨论。
自己太菜了没办法呀,自己想绝对想不到
偷偷放上超哥 的代码 应该不会被发现的,,,哈哈哈
//
//#include<cstdio>
//#include<cstring>
//#include<algorithm>
//using namespace std;
//const int maxn = 3e3+5;
//int n,m;
//int a[maxn],b[maxn],fa[maxn],ta,tb;
//long long ans=0,dp[maxn][maxn];
//
//int Find(int x) {
// return fa[x]==-1?x:fa[x]=Find(fa[x]);
//}
//void Union(int x,int y) {
// x=Find(x); y=Find(y);
// if (x!=y) {
// fa[y]=x;
// a[x]=min(a[x],a[y]);
// b[x]+=b[y];
// b[y]=0;
// }
//}
//int main()
//{
// memset(fa,-1,sizeof fa);
// scanf("%d%d",&n,&m);
// for (int i=1;i<=n*3;i++) {
// scanf("%d",&a[i]); b[i]=1;
// }
// for (int i=1;i<=m;i++) {
// int x,y;
// scanf("%d%d",&x,&y);
// Union(x,y);
// }
// for (int i=1;i<=n*3;i++) {
// if (b[i]==1) a[ta++]=a[i];
// if (b[i]==2) b[tb++]=a[i];
// if (b[i]==3) ans+=a[i];
// if (b[i]>=4) return 0*puts("-1");
// }
// sort(a,a+ta);
// sort(b,b+tb);
//
// memset(dp,-1,sizeof dp);
// dp[0][0]=0;
// for (int i=0;i<ta;i++)
// for (int j=0;j<tb;j++)
// if (dp[i][j]!=-1){
// dp[i+3][j]=max(dp[i+3][j],dp[i][j]+a[i]);
// dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+min(a[i],b[j]));
// }
// if (dp[ta][tb]==-1)
// printf("-1\n");
// else
// printf("%lld\n",ans+dp[ta][tb]);
//
//}