『贪心·动态规划』分组

本文探讨了一种分组策略,当人员的权重不同时,权重大的匹配权重小的。若权重相同,p值较小的作为组长。通过排序和动态规划的方法,实现了高效地匹配组员与组长,确保组的构建。时间复杂度分析显示,最坏情况下为O(105 * k),其中2k ≤ n,且n * k ≤ 105。
摘要由CSDN通过智能技术生成

P r o b l e m \mathrm{Problem} Problem

在这里插入图片描述
在这里插入图片描述

S o l u t i o n \mathrm{Solution} Solution

可以想到,当任意两个人权值不同时,一定是w值大的匹配w值小的。

w w w值相同时,则有 p = 1 p=1 p=1时匹配 p = 2 p=2 p=2 3 3 3 p = 3 p=3 p=3匹配 3 3 3 2 2 2.因此我们当 p = 2 p=2 p=2时赋值为 p = 4 p=4 p=4,那么就有 p p p小的为组长, p p p大的为组员。我们就按照 p p p w w w进行排序即可。

这样排序以后就有后面的当组长,前面的当组员。

我们设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示前 i i i个人,有 j j j个组完成了匹配,还有 k k k个人当组员且没有找到组长。

当前 p = 1 p=1 p=1 3 3 3时,有: f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j − 1 ] [ k + 1 ] + s [ i ] f[i][j][k]=f[i-1][j-1][k+1]+s[i] f[i][j][k]=f[i1][j1][k+1]+s[i]
表示新建了一个组。
p = 3 p=3 p=3 p = 4 p=4 p=4时,有: f [ i ] [ j ] [ k ] = f [ i − 1 ] [ j ] [ k + 1 ] + s [ i ] f[i][j][k]=f[i-1][j][k+1]+s[i] f[i][j][k]=f[i1][j][k+1]+s[i]
表示新增了一个组员.

我们发现时间复杂度为 O ( n k 2 ) O(nk^2) O(nk2).

有解就需要满足 2 k ≤ n 2k\le n 2kn,有因为 n k ≤ 1 0 5 nk\le 10^5 nk105,所以时间复杂度最大为 O ( 1 0 5 × k ) O(10^5\times k) O(105×k),大约为: O ( 1 0 5 1 0 5 ) O(10^5\sqrt {10^5}) O(105105 )

#include <cstdio>
#include <iostream>
#include <algorithm>

#define int long long

using namespace std;
const int N = 3000;

int n, K;
struct node {
	int w, s, p;
	friend bool operator < (node p1,node p2) {
		if (p1.w == p2.w) return p1.p > p2.p;
		return p1.w < p2.w;
	}
} a[N]; 
int f[2][N][N];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (c < '0' || c > '9') w |= c == '-', c = getchar();
	while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

signed main(void)
{
	n = read(), K = read();
	if (K * 2 > n) return puts("-1"), 0;
	for (int i=1,t;i<=n;++i)
	{
		a[i].w = read(), a[i].s = read();
		a[i].p = read();
		if (a[i].p == 2) a[i].p = 4;
	}
	sort(a+1,a+n+1);
	for (int i=0;i<N;++i)
	    for (int j=0;j<N;++j)
	        f[0][i][j] = f[1][i][j] = 1e17;
	f[0][0][0] = 0;
	int ans = 1e17;
	for (int t=1;t<=n;++t)
	{
		int i = t & 1; 
		for (int j=0;j<=min(K,t/2);++j) 
		    for (int k=0;k<=min(t-j*2,K);++k) 
		    {
		    	f[i][j][k] = f[i^1][j][k];//不选 
		    	if ((a[t].p == 3 || a[t].p == 4) && k) f[i][j][k] = min(f[i][j][k],f[i^1][j][k-1]+a[t].s);//当组员 
		    	if ((a[t].p == 1 || a[t].p == 3) && j) f[i][j][k] = min(f[i][j][k],f[i^1][j-1][k+1]+a[t].s);//组长 
			}
	}
	if (f[n&1][K][0] == 1e17) cout<<-1<<endl;
	else cout<<f[n&1][K][0]<<endl;
	return 0; 
}  
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值