连续攻击游戏
这道题目其实就是上面一道“超级英雄”
这里将属性作为左部,装备作为右部就好了
其实我最开始是没有想到的,因为我一直在想把某一个对象作为二分图的节点
这道题目就启发我们,其实不是非要把一个对象作为二分图的节点的,我们还可以把两个对象分别作为二分图的左右部来考虑(转换对象法)
另外这道题目的\(N\)非常大,我们需要改善一下匈牙利算法,就是将\(vis\)数组改成\(int\)类型,然后在每次dfs的时候多加一个参数\(t\),表示这是第几次dfs,然后在dfs里面判断\(vis\)是否等于\(t\)即可,这样就可以省去for循环的第一句memset了(注意dfs过程中\(vis\)不是加一,而是直接令成\(t\))
然后时间复杂度就正确了,可以从交错树的角度分析
注意交错树每一层的节点的属性都是相同的(指是左部节点还是右部节点),而且对于右部节点的层,每个节点只有唯一一个儿子,也就是说交错树的规模是左部节点的两倍,于是我们分析左部节点就好了,由于有\(vis\)标记,所以交错树的规模最多为\(10000\),而最多寻找\(10000\)次增广路,于是时间复杂度就正确了(也就是说以后做二分图匹配的时候,让左部为数量更少的点的时间复杂度更优秀)
另外还有并查集解法,复习的时候做一下



思路正确性:对于没有环的连通块,我们任选\(p-1\)个点,相当于任选一个点让其不能被选,将这个点作为树根,不难发现可以找到一种合法的分配方案;若连通块有环,可以将其看做基环树再加上若干条边,显然可以找到一种合法的分配方案;而每次合并让更大的点当根也是比较显然的贪心
update 2024.5.13
并查集做法的思路是对的,但是合并的时候代码是错的
hack数据:
3
1 2
1 2
2 3
错误原因也比较显然,就不赘述了
正确做法应该是记录点数和边数,只要边数不少于点数,那么就说明有环(注意这是一个连通集,没有环当且仅当边数为点数减一)
这个思路的来源就是转换对象法,将属性作为考虑对象,然后尝试转化为图论,由于只给了两个属性值,就相当于告诉了边的起点和终点了(要对\(2\)这个数字敏感)
这题的并查集也算个经典模型吧,相当于把每条边要么分给其起点,要么分给其终点,只能分给一个点
update 2026.2.22
想到了转换对象法,从装备转换到去考虑属性,但是还是没有想出这种转换成图论的做法
还是对2敏感一点吧
但是想到了一种新的做法:
维护一个大小为\(10000\)的数组\(c\),表示每个属性有多少个装备可以是这个属性
对于一个属性为\(a\)和\(b\)的装备,让\(c[a]\)和\(c[b]\)分别加一
显然答案具有单调性,二分答案\(x\),只用考虑\(c\)中下标在\(x\)之前的数
将\(c\)复制成\(d\)
显然\(d\)中下标在\(x\)之前的数中,如果\(\exists i∈[1,x],d[i]=0\),那么就不行
如果\(\exists i∈[1,x],d[i]=1\),那么显然这个\(i\)的优先级很高
于是就可以得出一种贪心的做法:优先考虑每个值为\(1\)的\(d\),将这个\(d\)唯一能够拥有的装备分配给他,同时让这个状态的另一个属性值所代表的\(d\)减一,并且调整各个\(d\)的顺序。如果中途发现存在一个还没有分配的\(d\)的值变成0了,那么就不行;如果某一个时刻最小的\(d\)的值大于1了,那么肯定可以,因为可以用数学归纳法证明,接下来按照每次选取最小的\(d\)进行上面的操作,在任意时刻至多会存在一个\(d\)的值为1,所以最后一定可以分配完
调整顺序可以使用优先队列,队列中的每个元素是一个三元组,代表属性,\(d\)值以及时间戳;为每个属性维护一个最新的时间戳,如果发现当前三元组的时间戳不是最新的那么直接弹出
这个做法其实就是对图做“度为 1 的点优先剥叶子”的可行性判定

浙公网安备 33010602011771号