CCPC2023-秦皇岛 J题题解

原题

思路

发现官方题解是枚举子集dp,但是我写的爆搜跑得也不慢,于是写一篇简单好写的搜索题解~

注意到  “如果她今天学习了一个长度为 k 的单词,她坚持要在当天记住词典 中所有长度为 k 的单词。” 

于是我们考虑先把相同长度的单词压到一起,方便后边处理

接下来开始写dfs

令 dfs(now,num,ww) 表示目前考虑学习第 now 个单词,目前学了 num 天,第 num + 1 天已经学了 ww 个单词

如果第 num + 1 天还能学单词,显然我们不知道学哪种是最优的,我们就把今天还能学的都搜一遍

特别的,如果第 num + 1 天学习了这个单词后, ww == w ,即第 num + 1 天一个单词也学不了了,需要特殊处理

如果第 num + 1 天已经学不了任何长度的单词了,那么我们无论选择哪个单词放在第 num + 2 天学,都可以搜到最优解,随便选一个即可

现在已经可以跑过了,亲测156ms,大概是正解的两倍,但也挺快的

还有一个小剪枝,即如果 num + 1 >= ans ,那么目前的这种情况肯定不是最优的,直接 return 掉(但是好像也没优化多少

这种想法,大概是暴力枚举全排列的优化版本,去除了不少重复情况

代码如下

#include <bits/stdc++.h>

#define int long long

#define maxn 100005

#define INF LONG_LONG_MAX

using namespace std;

int n,w;

int a[500005];

int tong[15];//长度i几个单词 

int cnt; 

bool pd[15];

int rt[15];

int ans;

void dfs(int now,int num,int ww){
	
	if(now == cnt + 1){
		
		ans = min(ans,num + (int)(ww != 0));
		
	} else {
		
		if(num + (int)(ww != 0) >= ans) return ;
		
		bool f = 0;
		
		for(int i = 1;i <= cnt;++ i){
			
			if(pd[i]){
				
				continue;
				
			} else if(ww + rt[i] <= w){
				
				pd[i] = 1;
				
				if(ww + rt[i] == w) dfs(now + 1,num + 1,0);
				
				else dfs(now + 1,num,ww + rt[i]);
				
				pd[i] = 0;
				
				f = 1;
				
			}
			
		}
		
		if(!f){
			
			for(int i = 1;i <= cnt;++ i){
				
				if(pd[i]) continue;
				
				pd[i] = 1;
				
				dfs(now + 1,num + 1,rt[i]);
				
				pd[i] = 0;
				
				return ;
				
			}
			
		}
		
	}
	
}

signed main(){
	
	ios::sync_with_stdio(0);
	
	cin.tie(0);
	
	cout.tie(0);
	
	cin >> n >> w;
	
	for(int i = 1;i <= n;++ i) cin >> a[i],tong[a[i]] ++;
	
	for(int i = 1;i <= 13;++ i){
		
		if(tong[i]){
			
			rt[++ cnt] = tong[i];
			
		}
		
	}
	
	ans = n;
	
	dfs(1,0,0);
	
	cout << ans << "\n";
	
	return 0;
	
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值