[ZROJ-954]分组 Solution

题意:
给n个人,每个人有自己的意愿以及工作经验,11表示能当组长,22表示能当组员,33表示既能当组长又能当组员,并且有雇佣他的花费,现在需要将这些人分成kk组,每组22个人,2×kn2\times k\leq n,每个组有11个组长和11个组员,要求组长的工作经验不小于组员的工作经验,请问组成kk个队的最小花费,或者这是不可能的。
考虑DP,首先要满足组长经验不小于组员,我们可以将这些人重新编号,如果他想当组长就编号为33,两个都可以就编号22,否则编号11,然后按照工作经验为第一关键字,编号为第二大关键字排序,设f[i][j]f[i][j]表示现在编好了ii个组,还剩下jj个组员可以选择。设当前人花费为costcost,转移分三种情况考虑:

  • 当前人编号为33f[i+1][j1]=min(f[i+1][j1],f[i][j]+cost)f[i+1][j-1]=\min(f[i+1][j-1],f[i][j]+cost)
  • 当前人编号为11f[i][j+1]=min(f[i][j+1],f[i][j]+cost)f[i][j+1]=\min(f[i][j+1],f[i][j]+cost)
  • 当前人编号为22:综合上面两种情况转移。
    code:code:
#include <bits/stdc++.h>
#define int long long
#define regi register int
int n,k;
long long f[5010][5010];
struct people{
	int exp;
	int cost;
	int wonder;
}p[1000001];
inline int read(){
	int r=0,w=0,c;
	for(;!isdigit(c=getchar());r=c);
	for(w=c^48;isdigit(c=getchar());w=w*10+(c^48));
	return r^45?w:-w;
}
bool cmp(people x,people y){
	return x.exp==y.exp?x.wonder<y.wonder:x.exp<y.exp;
}
main(){
	n=read(),k=read();
	if(n<k*2){
		puts("-1");
		return 0;
	}
	for(regi i=1;i<=n;++i){
	  p[i]={read(),read(),read()};
	  if(p[i].wonder==1)
	    p[i].wonder=3;
	  else if(p[i].wonder==2)
	    p[i].wonder=1;
	  else if(p[i].wonder==3)
	    p[i].wonder=2;
	}
	std::sort(p+1,p+n+1,cmp);
  for(regi i=0;i<=k+1;++i)
    for(regi j=0;j<=k+1;++j)
      f[i][j]=0x3f3f3f3f3f3f3f3fLL;
  f[0][0]=0;
	for(regi i=1;i<=n;++i)
	  for(regi j=k;j>=0;--j)
		  for(regi u=k;u>=0;--u){
		  	if(p[i].wonder==3&&j<k&&u)
		  	  f[j+1][u-1]=std::min(f[j+1][u-1],f[j][u]+1LL*p[i].cost);
		  	if(p[i].wonder==1&&u<k)
		  	  f[j][u+1]=std::min(f[j][u+1],f[j][u]+1LL*p[i].cost);
		  	if(p[i].wonder==2){
		  		if(j<k&&u)
					  f[j+1][u-1]=std::min(f[j+1][u-1],f[j][u]+1LL*p[i].cost);
		  	  if(u<k)
					  f[j][u+1]=std::min(f[j][u+1],f[j][u]+1LL*p[i].cost);
		  	}
		  }
	printf("%lld\n",f[k][0]==0x3f3f3f3f3f3f3f3fLL?-1LL:f[k][0]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章