【折半搜索】洛谷P3067:[USACO12OPEN]Balanced Cow Subsets G

news/2024/11/2 16:29:52/

先看题:

 P3067 [USACO12OPEN]Balanced Cow Subsets G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

说实话,洛谷的题解的确是有点难懂,所以本菜在这里写一个比较易懂的思路和代码:

首先先说用什么算法,这里用的是折半搜索,即先搜索0~n/2-1,然后将我们需要的信息记录下来,然后我们在搜索n/2~n-1,用得到的信息与0~n/2-1得到的信息进行比对,然后寻找答案;

【说实话,理解折半搜索的好方法是去写leetcode里的《三数之和》,其根本是空间换时间,但仅仅知道这一点还是不够】

这里我们先抽象一下题目:

我们想象是给每头牛一个权值【英语不好的可以百度翻译喔】,这个权重可以是0,-m,m,【m为一头牛的产奶量】,然后依次给完每个权值后,要求每头牛的权值相加为0,问这种方案有多少种;

抽象完题目后就简单了,我们进行搜索的方法也有了,即每一头牛的状态有3种,给0权,给负权,给正权,这里就对应dfs搜索一个节点的3个分支。

所以dfs中有一个形参确认了,即sum,这里sum记录的是已经给出去的权值相加起来是多少;

当然,还有一个形参c,代表dfs进入多少层了;

那么第一次循环我们要记录什么信息呢??

说这一点前,我们先考虑怎么连接两次搜索,即怎么用第一次搜索的结果来结合第二次搜索寻找答案;

想搞清楚这个问题,我们找的是什么,这个很容易想到,在前面就说了,我们要找和为0的方案;

那好,我们已经知道了任意给权的情况下,前半部分的权值和,那么如果我们进行第二次搜索,得到一种情况A【这里的A情况只考虑n/2~n-1这些牛的给权情况】,A情况下的权和为sum,那么我们只需要找到前半部分和为-sum的情况个数,然后就可以确认一种情况了;

这里要考虑用什么容器,因为我们要给出一个值,然后快速找到对应的值,这里毫无疑问用unordered_map,即我们用第二次搜索得到的sum来确认第一次搜索的结果;

但是我们还得考虑到一种情况,即题目说的是拿出任意头牛进行给权,但是,拿出来的牛可能有多种给权为0的方式;

比如 1 2 4 2 1   给权可以为  0 2 0 -2 0,也可以为 -1 0 0 0 -1,但是由题意可知,这几头牛是一样的,所以为一种方案,所以我们这边要去重,对于这种多对一的去重,我们可以用id码来去重,即找到上面12421这个放案所有给权后和为0的方案的共同点,然后根据这个共同点来去重;

这个共同点很容易找到,即这些方案选的牛的编号都是相同的,然后我们可以给第一次搜索得到的方案一个编号,即将选哪些牛的状态压缩到一串二进制数里,1为选,0为不选;

然后第二次搜索也给一个id号,这一前一后两个id号就可以结合出一个唯一的id号了,而且这个id号对应的十进制数不大,可以用数组来装下,即st[i]=1表示id号为i的选择方案可行,即按i对应的二进制去选牛的话可以得到一个可行方案;【这里用unordered_map的话会超时】

好,那么讲完了后可以看懂以下代码了【洛谷记得开氧气优化喔】

#include<iostream>
#include<algorithm>
#include<vector>
#include<unordered_map>
using namespace std;const int N=1<<21;int val[N];//记录每一头牛的产奶量
bool ha[N];//ha[i]=1表示id号为i的方案可行,=0时表示不可行
unordered_map<int,vector<int>> fix;//记录和为int的值的id
int n;
int ans;void dfs1(int sum,int id,int c){//sum为当前的和,id为当前状态的编号,c为当前递归层数if(c==n/2){fix[sum].push_back(id);//将这个值push到对应的位置return;}dfs1(sum,id,c+1);//给0权dfs1(sum+val[c],id+(1<<c),c+1);//给正权,并将对应的牛在二进制的位置赋值为1dfs1(sum-val[c],id+(1<<c),c+1);//给负权,并将对应的牛在二进制的位置赋值为1
}void dfs2(int sum,int id,int c){if(c==n){vector<int> mid=fix[-sum];//mid里存储着第一次搜索里和为-sum的id号for(int i=0;i<mid.size();i++){if(!ha[id+mid[i]]&&id+mid[i]) ans++,ha[mid[i]+id]=1;//id号为0时为一头牛都不选,由题意可知是不可行方案,但是其权和为0,所以要特判}return;}dfs2(sum,id,c+1);dfs2(sum+val[c],id+(1<<c),c+1);dfs2(sum-val[c],id+(1<<c),c+1);
}int main(){cin>>n;for(int i=0;i<n;i++) cin>>val[i];dfs1(0,0,0),dfs2(0,0,n/2);cout<<ans;}


http://www.ppmy.cn/news/919228.html

相关文章

洛谷[P3374] [P3368]树状数组(线段树解法)

最近学习了线段树&#xff0c;不会树状数组&#xff0c;用线段树舔着脸来做一下 P3374树状数组1https://www.luogu.com.cn/problem/P3374 ac代码 #include<bits/stdc.h> using namespace std; const int N 5e7 10;//N开四倍空间会re&#xff0c;所以开大了点 #defin…

洛谷 P4467 [SCOI2007]k短路

具体题目见洛谷 P4467 方法一&#xff1a;A* 思路&#xff1a;A* 算法目标状态第一次被取出时即为最优解&#xff0c;根据这个特点&#xff0c;我们只需要取出第 k 个目标状态就是答案。 又因为 A* 算法的估值尽量与正确值相似且必须 ≤ ≤ ≤ 正确值的要求。这里使用 SPFA…

再度合作|极智嘉(Geek+) P系列拣选机器人进驻CEVA欧洲物流中心

近日&#xff0c;3PL巨头CEVA Logistics首度在社交媒体展示其与极智嘉(Geek)合作打造的全新欧洲物流中心&#xff0c;并在推文中对极智嘉(Geek)给予了高度赞扬。CEVA表示&#xff0c;极智嘉不仅彻底颠覆了CEVA的工作环境&#xff0c;还充分保障了敬业员工的安全和人体工程学效率…

洛谷P3373线段树

洛谷P3373 线段树模板题&#xff0c;主要对懒标的处理要求比较高。 有三种操作&#xff1a; 区间加法区间乘法区间求和查询 tips&#xff1a;我们对一个区间进行乘k操作的时候&#xff0c;他之前可能存在加法lazy还没pushdown&#xff0c;这时候&#xff0c;加法lazy和乘法la…

洛谷P3637题解

从题目就可以看出来&#xff0c;这是一道典型的动规题。 由于是最大上升子序列&#xff0c;所以对任意一个数&#xff0c;在其之后比其大的都不能具有比其更长的子序列&#xff0c;因此每当序列中出现一个更小的数&#xff0c;我们只用考虑以后比其更小的数即可&#xff0c;再…

leetcode 636. Exclusive Time of Functions | 636. 函数的独占时间(Stack)

题目 https://leetcode.com/problems/exclusive-time-of-functions/ 题解 类似于括号匹配问题&#xff0c;遍历 list&#xff0c;每一次来到新元素时&#xff0c;结算当前正在执行的函数已经运行的时间&#xff0c;累加到 result 数组的对应位置中。怎么知道当前正在执行哪…

LeetCode347. 前 K 个频繁元素

LeetCode347. 前 K 个频繁元素 题目 给定一个非空的整数数组&#xff0c;返回其中出现频率前 k 高的元素。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2]示例 2: 输入: nums [1], k 1 输出: [1]说明&#xff1a; 你可以假设给定的 k 总是合理的&#xff0c;且…

使用PyMOL绘制蛋白与配体分子结合模式图

更好的阅读体验&#xff1a;使用PyMOL绘制蛋白与配体分子结合模式图 摘要 本文将带大家使用免费开源的PyMOL软件绘制D715-2441蛋白与与PB2cap配体分子的结合模式图。 我们先来看一张成品图&#xff1a; 研究背景 根据分析结果&#xff0c;我们获得以下相互作用&#xff1…