Jellyfish and Mex

题目传送门

按理说,不应该想不出来,我还是太弱了,DP黑洞

解法

首先有两个性质:
1.不会删比当前 m e x mex mex 大的数
2.要删某个数一定是一删到底,直到这个数删完

状态我是真的设计不来,要积累经验呀

状态设计 :

f i : 考虑 0 至 i − 1 的数,即当前 m e x = i , 删完所有数的代价 f_{i}:考虑0至i-1的数,即当前 mex=i,删完所有数的代价 fi:考虑0i1的数,即当前mex=i,删完所有数的代价

状态转移 :

显然状态到 0 0 0 就结束( m e x = 0 mex=0 mex=0 是无代价)
枚举删哪个数转移
c n t j cnt_j cntj 为数字 j j j 的出现次数
f i = { 0 ( i = 0 ) f j + j + ( c n t j − 1 ) ∗ i ( i ≠ 0 ) f_i = \begin{cases} 0 &(i=0)\\ f_j+j+(cnt_j-1)*i &(i\ne0) \end{cases} fi={0fj+j+(cntj1)i(i=0)(i=0)

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>

#define rep(i, j, k) for (register int i = j; i <= k; i++)
#define _rep(i, j, k) for (register int i = k; i >= j; i--)
#define _b_pct __builtin_popcount

using ll = long long;
using db = double;
using namespace std;

const int N = 1e4 + 7, M = 2e6 + 7, K = 16, INF = 1e9;
const ll inv2 = 499122177, LLF = 1e16, mod = 1e9 + 7;

int n,mex;
int a[N],cnt[N],f[N];

void pre() {
	
}

void init() {
	memset(f,0x3f,sizeof f);
	memset(cnt,0,sizeof cnt); 
	f[0]=0; 
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		if(a[i]<=5200) cnt[a[i]]++;
	}
	for(int i=0;i<=5200;i++) 
		if(cnt[i]==0) {mex=i; break;}
}

void work() {
  	for(int i=1;i<=n;i++) 
		for(int j=0;j<n;j++) 
			f[i]=min(f[i],f[j]+j+i*(cnt[j]-1));
	printf("%d\n",f[mex]);
}
int main() {
    // freopen("C.in", "r", stdin);
    // freopen("C.out", "w", stdout);
    int T; scanf("%d",&T); 
    while (T--) {
        init();
        work();
    }
}

万事从模仿开始
万事以创新开始

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值