12.13-21刷题汇总
P1115 最大字段和
第一种 贪心法:
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n;
ll a[200005];
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
ll max_sum=-0x3f3f3f3f,tp_sum=-0x3f3f3f3f;
for(int i=0;i<n;i++)
{
if(tp_sum<0)
tp_sum=a[i];
else
tp_sum+=a[i];
if(tp_sum>max_sum)
max_sum=tp_sum;
}
cout<<max_sum;
return 0;
}
动态规划:dp[i]表示以a[i]结尾的最大子序和,那么dp[i]=max(dp[i-1]+a[i],a[i])
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n;
ll a[200005];
ll dp[200005];
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
ll max_sum=-0x3f3f3f3f;
dp[0]=a[0];
for(int i=1;i<n;i++)
{
dp[i]=max((dp[i-1]+a[i]),a[i]);
if(dp[i]>max_sum)
max_sum=dp[i];
}
cout<<max_sum;
return 0;
}
P1725 琪露诺
这题咋一看可以用BFS解决 但是没有很明显的剪枝策略 感觉时间复杂度会爆掉
先莽一波(毕竟正解是dp+单调队列)
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, l, r;
ll a[200005];
ll value[200005];
queue<ll> q;
int main()
{
cin >> n >> l >> r;
for (int i = 0; i <= n; i++)
cin >> a[i];
memset(value, -0x3f3f3f3f, sizeof(value));
q.push(0);
value[0] = 0;
ll max_sum = -0x3f3f3f3f;
// for (int i = 1; i <= n; i++)
// cout << value[i] << ' ';
// cout << endl;
while (!q.empty())
{
ll tp = q.front();
q.pop();
for (int i = l; i <= r; i++)
{
if ((tp + i) > n) // 下一步能到河对岸
{
if (value[tp] > max_sum)
max_sum = value[tp];
break;
}
if (value[tp] + a[tp + i] > value[tp + i])//如果跳到这个格子上的时候发现总能量更大,那这个格子重新入队,更新这个格子能到达的格子的value
{
value[tp + i] = value[tp] + a[tp + i];
q.push(tp + i);
}
}
// for (int i = 1; i <= n; i++)
// cout << value[i] << ' ';
// cout << endl;
// cout << "max_sum" << max_sum << endl;
}
cout << max_sum;
return 0;
}
没剪枝是这样的。时间爆掉辣
回来再看一下dp+单调队列怎么写,现在去搞免修的那个表。
泰无语辣用单调队列写了好几天,最后发现是单调队列的数据结构用错了,由于单调队列要删头去尾,所以必须用双端队列,我写成普通的队列了,我说怎么一直0分。
还是简单写一下这题的思路吧
- 动态规划,如果跳到格子a[i]的最大价值为dp[i],我们不难发现肯定会有dp[i]一定是由能跳到a[i]的格子中的最大价值加上a[i]组成的,所以状态转移方程就是dp[i]=max{d[j]}+a[i],其中不难发现能跳到i的格子分别是i-R到i-L。
- 所以接下来我们把格子从1到n都扫一遍,每扫到一个元素i都会对应一个窗口(从i-R到i-L),而我们需要找到其中的最大值所以这里面很明显又可以用单调队列去优化。其中我们可以发现随着i从1到n增大,下一个入队的元素一定是i-L,所以这题单调队列的维护还是很简单的。
代码如下
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, l, r;
ll a[200005];
ll dp[200005];
deque<ll> q;
int main()
{
cin >> n >> l >> r;
for (int i = 0; i <= n; i++)
cin >> a[i];
ll ans = -0x3f3f3f3f;
for (int i = 1; i < l; i++)//把跳不到的地方用无穷小考虑,确保不会由这些格子跳到下一个格子
dp[i] = -0x3f3f3f3f;
q.push_back(0);
for (int i = l; i <= n; i++)
{
while (!q.empty() && dp[q.back()] < dp[i - l])
q.pop_back();
while (!q.empty() && q.front() < i - r)
q.pop_front();
q.push_back(i - l);
dp[i] = a[i] + dp[q.front()];
if (i + r > n)
ans = max(ans, dp[i]);
// for (int j = l; j <= i; j++)
// cout << dp[j] << ' ';
// cout << endl;
}
cout << ans;
return 0;
}
P3957 跳房子
哇呜呜 虽然这题只是二分+dp+单调队列,但是但是我debug真的花了特别特别特别久,主要是迭代mid的时候忘记初始化dp和队列q了,导致了很多错误,也简单说一下这题的思路吧
- 其实这题的思路和琪露诺很像,有两点要注意,第一,这题要用二分查找答案的方法找到最少的符合条件的花费;其次,这题的格子不再连续,也就是说单调队列的维护变得困难了一些,即扫描到第i个元素的时候谁入队?会不会没有元素入队?这时候我采用了用last标记最后一个未入队元素的方法来解决这个问题,随着last的递增也能确保每一个元素只入队一次,从而达到实现单调队列的目的。
- 此外,这题还有个很坑爹的问题,就是dp的最小值(为了防止无法到达的格子的dp值影响最后judge的判断所设置的负无穷大)要设置成-1e18,我一开始只设为-0x3f3f3f3f只能拿90分=-=
这也是第一次ac蓝题
代码如下:
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, d, k;
struct node
{
ll index;
ll value;
};
node a[500005];
ll dp[500005];
deque<ll> q;
bool judge(ll g)
{
memset(dp, 0, sizeof(dp));
q.clear();
// cout << "g:" << g << endl;
q.push_back(0);
ll last = 1; // 目前入队的最 后一个元素的下一个元素
ll right = g + d;
ll left = d > g ? d - g : 1;
for (int i = 1; i <= n; i++)
{
// cout << "i:" << i << endl;
if (a[i].index < left)
dp[i] = -1e18;
else // 开始能跳到
{
ll in_scale = last; // 用in_scale 找到下一个要入队的元素,即不在队列中但是能跳到i
ll i_left = a[i].index - right, i_right = a[i].index - left; // 确定能到达i的区间范围
while (a[in_scale].index < i_left)
{
in_scale++;
}
// cout << "inscale" << in_scale << endl;
if (a[in_scale].index <= i_right) // 说明有新元素要入队
{
// cout << "haha" << endl;
while (a[in_scale].index <= i_right)
{
while (!q.empty() && dp[q.back()] < dp[in_scale])
q.pop_back();
while (!q.empty() && a[q.front()].index < i_left)
q.pop_front();
// cout<<"queue:";
// for(auto it : q)
// cout<<it<<' ';
// cout<<endl;
q.push_back(in_scale);
// cout << "front " << q.front() << ' ' << "dp[q.front()] " << dp[q.front()] << endl;
dp[i] = a[i].value + dp[q.front()];
// for (int i = 1; i <= n; i++)
// cout << dp[i] << ' ';
// cout << endl;
if (dp[i] >= k)
return true;
in_scale++;
}
last = in_scale;
}
else // a[in_scale].index>i-d+g 下一个元素超过右端 没有元素要入队
{
// cout << "na" << endl;
while (!q.empty() && a[q.front()].index < i_left)
q.pop_front();
if (q.empty()) // 此时队列删头之后为空,说明无法到达
dp[i]=-1e18;
else
{
// cout << "front " << q.front() << ' ' << "dp[q.front()] " << dp[q.front()] << endl;
dp[i] = a[i].value + dp[q.front()];
// for (int i = 1; i <= n; i++)
// cout << dp[i] << ' ';
// cout << endl;
if (dp[i] >= k)
return true;
}
}
// cout << "last " << last << endl;
}
}
return false;
}
int main()
{
cin >> n >> d >> k;
for (int i = 1; i <= n; i++)
cin >> a[i].index >> a[i].value;
ll left = 0, right = 1e9, flag = 0;
ll g;
while (left <= right)
{
ll mid = (left + right) / 2;
if (judge(mid)) // 钱花多了
{
flag = 1;
g = mid;
right = mid - 1;
}
else
left = mid + 1;
// cout<<'b'<<endl;
}
// if (judge(0))
// flag = 1;
if (flag)
cout << g;
else
cout << -1;
return 0;
}
P2776 小组队列
一道很简单的二维队列的题目(我还因为偷了一点小懒卡了一会哇呜呜
代码如下:
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, m;
ll a[100005];
queue<ll> q[305];
queue<ll> turn;
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> a[i];
ll t;
cin >> t;
while (t--)
{
string op;
cin >> op;
if (op == "push")
{
ll num;
cin >> num;
if (q[a[num]].empty())
{
turn.push(a[num]);
}
q[a[num]].push(num);
}
else
{
ll first = turn.front();
cout << q[first].front() << endl;
q[first].pop();
if(q[first].empty())
turn.pop();
}
}
return 0;
}
P2947 向右看齐
很简单的一道单调栈的题目
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n;
ll a[100005];
ll ans[100005];
stack<ll> st;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
ans[n] = 0;
st.push(n);
for (int i = n - 1; i >= 1; i--)
{
while (!st.empty() && a[st.top()] <= a[i])
st.pop();
if (st.empty())
ans[i] = 0;
else
ans[i] = st.top();
st.push(i);
}
for (int i = 1; i <= n; i++)
cout << ans[i] << endl;
return 0;
}
P1739 括号表达式匹配
居然能刷到入门题
很简单的一道题 代码如下:
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
string s;
stack<char> st;
int main()
{
cin >> s;
ll flag = 1;
for (auto it : s)
{
if (it == '(')
st.push('(');
else if (it == ')')
{
if (st.empty())
{
flag = 0;
break;
}
else
st.pop();
}
else
continue;
}
if(!st.empty())
flag=0;
if (flag)
cout << "YES";
else
cout << "NO";
return 0;
}
该有怎么样的努力才能配得上你高贵的梦想
P5018对称二叉树
这题一开始看的时候很懵逼,因为完全不知道怎么用递归去实现对于"对称"这个性质的检验,之后看了题解才恍然大悟。本题思路:
- 定义函数same,用于检查一个节点的左右子树是否对称,这个函数又可以分为以下几种情况来讨论:
- 如果左右节点都为空,那么对称
- 一个为空一个不为空,那么一定不对称
- 都不为空,检查value值是否相同,不相同一定不对称
- 若value值相同,那么递归检查左节点的左子树和右节点的右子树、左节点的右子树和右节点的左子树是否对称
- 定义函数cnt,用于统计从该节点出发的树的节点数目(这应该是常规知识)
代码如下:
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n;
struct node
{
ll value;
ll left, right;
};
node tree[1000005];
bool same(ll left, ll right)
{
if (left == -1 && right == -1)
return true;
else if (left == -1 || right == -1)
return false;
else
{
if (tree[left].value != tree[right].value)
return false;
else
return same(tree[left].left, tree[right].right) && same(tree[right].left, tree[left].right);
}
}
ll cnt(ll x)
{
if (x == -1)
return 0;
return 1 + cnt(tree[x].left) + cnt(tree[x].right);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> tree[i].value;
for (int i = 1; i <= n; i++)
cin >> tree[i].left >> tree[i].right;
ll ans = -1;
for (int i = 1; i <= n; i++)
{
if (same(tree[i].left, tree[i].right))
ans = max(ans, cnt(i));
}
cout << ans;
return 0;
}
P 2168 荷马史诗
这题其实是一道比较简单的k叉哈夫曼树编码,需要用到优先队列priorty_queue。思路如下:
- 首先由于是k叉哈夫曼树,要确定补0的个数,观察可知每个节点都挂满的k叉哈夫曼树应该有m*(k-1)+k个节点(其中m为树深h-1),首先确定比n恰好大一点的满k叉哈夫曼树的节点数m*(k-1)+k,两者作差就能得到补0的个数。
- 在每次队列操作中,选择队列中最前面的k个元素组成新的节点再入队。其中为了保证树深尽量小,所以节点高度小的节点的优先级会比节点高度大的优先级更高。
代码如下:
c++
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, k;
struct node
{
ll w;
ll f;
};
class compare
{
public:
bool operator()(node a, node b)
{
if (a.w > b.w)
return true;
if (a.w == b.w)
return a.f > b.f;
return false;
}
};
priority_queue<node, vector<node>, compare> q;
ll get_num() // 要添加的0的个数
{
ll tp = (n - k) / (k - 1);
if (tp * (k - 1) == n - k) // 整除
return 0;
else
return (k - 1) * (tp + 1) + k - n;
}
int main()
{
cin >> n >> k;
ll num0 = get_num();
ll sum = 0;
// cout << num0 << endl;
for (int i = 0; i < num0; i++)
q.push({0, 0});
for (int i = 0; i < n; i++)
{
ll w;
cin >> w;
q.push({w, 0});
}
while (q.size() >= 2)
{
ll max_f = -1, tp_sum = 0;
for (int i = 0; i < k; i++)
{
node p = q.top();
q.pop();
tp_sum += p.w;
max_f = max(max_f, p.f);
}
sum += tp_sum;
q.push({tp_sum, max_f + 1});
}
node ans = q.top();
cout << sum << endl;
cout << ans.f << endl;
return 0;
}