武汉大学“华为杯”补题
贪心 + 递归
这题我在做的时候根本没有理解清楚题意,也可能是受到样例解释的影响?
就比如n=10的时候,我就认为最大的节点编号是21,因为[1,10]一共可以构造21个子区间,所以最大的节点编号就是21,如图:
图中共有21个区间,但如果我们从做左到右,从上到下,把整棵树看做一个完全二叉树,其中区间[7,7]是最后一个区间,那么在完全二叉树当中,[7,7]就是第25个区间了,而题目的意思就是让我们让我们把给定的区间[1,n]展开成一棵树,然后把这棵树放在一个完全二叉树当中,计算这棵树在完全二叉树当中最大的节点编号!
由于完全二叉树的节点编号从左到右,从上到下是依次递增的,所以说,我们希望,这个最大节点位于最下一层的最右边的节点(上下深度的优先级更大)
并且,我们能得知这个最大节点是在左子树还是在右子树中,由于上下的优先级肯定是大于左右的。
- 所以当左子树的深度小于右子树时,肯定是右子树,当右子树深度小于左子树时,肯定是左子。
- 难点在于当深度相等的时候,我们应该选择哪一个区间。我们知道,在完全二叉树中,如果根节点值为root,那么左孩子就是2*root,右孩子就是2*root+1,显然右边的比左边要大,并且,如果我们选择了右边,那么在深度相同的时候,右边的本来就比左边大。(显而易见的事情)
现在让我们回顾一下递归的步骤
- 递归函数的功能:build(l, r, id)求给定区间[l,r],根节点编号为id时这个区间划分到完全二叉树的最大节点编号。
- 递归结束的条件:l = r,此时不能再划分区间,递归结束。
- 递归的转移:选择左区间:build(l, mid, id << 1);选择右区间:build(mid + 1, r, id << 1
| 1)
#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
int get_depth(int d)//d表示区间长度
{
int depth = 0;
while(d != 1)//当区间长度为1时不可在划分
{
d = (d + 1) >> 1;//每次划分保留左侧区间长度
//因为mid是下取整,所以左侧区间长度肯定大于右侧区间长度
//所以我们每次保留左侧区长度都就行了
depth ++ ;
}
return depth;
}
int build(int l, int r, int id)
{
if(l == r)//递归结束
{//此时区间不可以再划分
return id;//当前的id值就是最大值
}
//否则,二分一下左右区间,先看谁的深度更大
int mid = l + r >> 1;
int depth_left = get_depth(mid - l + 1);
int depth_right = get_depth(r - (mid + 1) + 1);
//递归方程转移
if(depth_right >= depth_left)
return build(mid + 1, r, id << 1 | 1);
else
return build(l, mid, id << 1);
}
signed main()
{
int T;
cin >> T;
while(T -- )
{
int n; cin >> n;
cout << build(1, n, 1) << endl;
}
return 0;
}
贪心 + BFS
这道题目的意思很简单,反向建图就行了
但是我一开始想的是按照拓扑序遍历图,但我们知道,拓扑序是不允许出现环的,但这道题目是有环的(样例2就是)
其实直接排序后贪心枚举最大的边就行了
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int N = 100010;
int n, m;
long long ans, w[N];
pair<int, int> p[N];
vector<int> g[N];
bool st[N];
void bfs(int u)
{
queue<int> q;
q.push(u);
st[u] = true;
while(q.size())
{
auto t = q.front(); q.pop();
int len = g[t].size();
for(int i = 0; i < len; i ++ )
{
int j = g[t][i];
if(st[j]) continue;
if(w[j] < w[t])
{
w[j] = w[t];
q.push(j);
st[j] = true;
}
}
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ )
{
cin >> w[i];
p[i] = {w[i], i};
}
sort(p + 1, p + n + 1);
while(m -- )
{
int a, b;
cin >> a >> b;
g[b].push_back(a);
}
for(int i = n; i >= 1; i -- )
{
if(!st[p[i].second]) bfs(p[i].second);
}
// for(int i = 1; i <= n; i ++ ) cout << w[i] << " ";
// cout << endl;
for(int i = 1; i <= n; i ++ ) ans += w[i];
cout << ans << endl;
return 0;
}