为保证满足经验值要求,可以按照经验值从小到大排序,当经验值相同时组长排在最后面,组员排在最前面,中间是(p=3)的员工
当这样排序时,每次放置组长到有人的组时保证经验值满足要求((>=的情况也考虑))了,且相当于每一个合法的可以匹配的组员,组长都考虑过了,这样就既满足了合法性又不会遗漏情况。显然这样的排序是正确的。
为什么设计只有组员的组合的数目而不考虑只有组长得组合的数目呢,因为需要保证经验的限制,也就是后面的人必须比前面的人经验值小,才能保证转移的合法性,所以这和从小到大排序只考虑只有组员的情况是等价的,换句话说,这样设计状态所有状况组合都已经考虑到了。
这道题难在排序和状态的设计
(dp)过程显然,若当前是组长,则可以考虑不选或加入已有的一组(特判是否有)
如果是组员则考虑不选或建新组(若超过(k)则不新建)
不选的时候状态继承
注意的几点:
1.(j,kd,j+kd<=i)
2.注意判断无解的情况((dp[n+1][k][0]=INF))
3.开(long long),(dp)数组第一维滚动优化
重要优化((40pts-->100pts))
所以(dp)数组开(2*600*600)即可
(Code)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define re register
#define N 1000100
#define ll long long
using namespace std;
int n,k;
const int maxn =1020;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct node{
int w,p;
ll s;
}pe[100010];
bool cmp(node A,node B)
{
if(A.w!=B.w) return A.w<B.w;
if(A.p==1) return 0;
else if(A.p==2) return 1;
else if(A.p==3&&B.p==1) return 1;
else if(A.p==3&&B.p==2) return 0;
}
ll dp[2][maxn][maxn];//dp[i][j][k]表示当前考虑到i个人
//已经组成了j组,还有k组只有组员的最小代价
void DP()
{
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[1][0][0]=0;
for(re int i=1;i<=n;++i)
{
for(re int j=0;j<i;++j)
{
for(re int kd=0;(kd+j<=k)&&(kd+j<i);++kd)
{
dp[(i+1)%2][j][kd]=min(dp[i%2][j][kd],dp[(i+1)%2][j][kd]);//不选择的情况
if(pe[i].p==1)
{
if(kd>0) dp[(i+1)%2][j+1][kd-1]=min(dp[(i+1)%2][j+1][kd-1],(ll)dp[i%2][j][kd]+pe[i].s);
}
else if(pe[i].p==2)
{
if(kd+1<=k)dp[(i+1)%2][j][kd+1]=min(dp[(i+1)%2][j][kd+1],(ll)dp[i%2][j][kd]+pe[i].s);
}
else
{
if(kd>0) dp[(i+1)%2][j+1][kd-1]=min(dp[(i+1)%2][j+1][kd-1],(ll)dp[i%2][j][kd]+pe[i].s);
if(kd+1<=k)dp[(i+1)%2][j][kd+1]=min(dp[(i+1)%2][j][kd+1],(ll)dp[i%2][j][kd]+pe[i].s);
}
}
}
}
}
int main()
{
n=read(),k=read();
if(n<=2*k){ printf("-1
");return 0; }
for(re int i=1;i<=n;++i)
{
pe[i].w=read();
pe[i].s=read();
pe[i].p=read();
}
sort(pe+1,pe+n+1,cmp);
DP();
if(dp[(n+1)%2][k][0]==4557430888798830399)
{
printf("-1
");
return 0;
}
printf("%lld
",dp[(n+1)%2][k][0]);
//printf("%d %d
",maxn,N);
return 0;
}
蒟蒻(RE80)分目前不知道怎么回事,有大佬知道请在评论区指点一下