七题题解
A
科普
getline
在 OI 中,经常用 getline(cin, s)
读入一行(包含空格)到 s
中。
string::find
假如有两个叫 a,b
的 string
。
那么 a.find(b)
返回 b
在 a
中第一次出现的位置的下标。
如果 b
没有在 a
中出现过,返回 -1
。
\
的转义字符
像换行是 \n
一样,在字符串中,\\
表示一个 \
。
代码
#include <iostream>
#include <string>
using namespace std;
string s;
int main()
{
getline(cin, s);
if(s.find("\\r\\n") != -1) cout << "windows";
else if(s.find("\\n") != -1) cout << "linux";
else cout << "mac";
return 0;
}
B
思路
把入栈的每一堆团子当成一个整体。
出栈时一堆一堆地往外出,在需要把一堆拆开时就拆开。
善用等差数列求和公式。
代码
有一些细节。
注意栈的判空。
#include <iostream>
#include <stack>
#include <utility>
#include <cstdio>
#define int long long
using namespace std;
int n;stack<pair<int, int> > s;
signed main()
{
cin >> n;
for(int i = 0, l, r, k, o;i < n;++i)
{
cin >> o;
if(o == 1) cin >> l >> r, s.push(make_pair(l, r)); //成堆入栈
else
{
cin >> k;int cnt = 0, ans = 0; //cnt记录拿了多少团子,ans记录拿出团子的总价值
while(!s.empty()) //注意
{
int l = s.top().first, r = s.top().second; //拿出栈顶的一堆团子
if(cnt + r - l + 1 > k) break; //再拿就超过k个了
cnt += r - l + 1;ans += (l + r) * (r - l + 1) / 2;s.pop(); //记录cnt和ans
}
if(s.empty()) //刚好把栈拿完,肯定不需要再拿了
{
cout << ans << endl;
continue;
}
//读者自行思考为什么没有上面的if会RE?
int l = s.top().first, r = s.top().second;s.pop(); //拿出栈顶的一堆团子
int lst = k - cnt; //还要拿多少个
ans += (r - lst + 1 + r) * lst / 2; //从这一堆团子中拿出还要拿的
r -= lst;s.push(make_pair(l, r)); //剩下的放回栈顶
cout << ans << endl;
}
}
return 0;
}
C
科普
to_string(int x)
:将整数 x
转化为字符串
思路
数据范围明显搜索题。
只需要搜索每一条路线,搜索时顺便记录路线即可。
代码
#include <iostream>
#include <string>
using namespace std;
struct Edge{int v, nxt;}edge[1050];
int n, a[50], head[50], cnt, maxn;string ans;
void add(int u, int v)
{
++cnt;
edge[cnt].v = v;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
void dfs(int x, int dep, string s) //x:当前节点,dep:当前地雷数,s:当前路线
{
s += "-" + to_string(x); //记录路线
for(int i = head[x];i;i = edge[i].nxt)
dfs(edge[i].v, dep + a[edge[i].v], s);
if(dep > maxn) maxn = dep, ans = s.substr(1, s.length() - 1);
}
int main()
{
cin >> n;
for(int i = 1;i <= n;++i)
cin >> a[i];
for(int l, r;;)
{
cin >> l >> r;
if(l == 0 && r == 0) break;
add(l, r);
}
for(int i = 1;i <= n;++i)
dfs(i, a[i], "");
cout << ans << endl << maxn;
return 0;
}
D
思路
我们把切木板的过程看成合并木板,每次合并的花费 = 两块木板的长度和。
合并果子相信都做过,贪心思路就不再说了。
代码
合并果子原代码能过。
#include <iostream>
#include <queue>
#define int long long
using namespace std;
priority_queue<int, vector<int>, greater<int> > q;int n, ans;
signed main()
{
cin >> n;
for(int i = 0, t;i < n;i++)
cin >> t, q.push(t);
while(q.size() != 1)
{
int a = q.top();q.pop();
int b = q.top();q.pop();
q.push(a + b);ans += a + b;
}
cout << ans;
return 0;
}
E
科普
stoi(string s)
:将 s
转化为整数。
思路
没啥好说的,纯模拟。
注意题目里的一句话
如果只是原地转弯,从开始到最后从来不走动,则输出“(0,0)”。
代码
#include <iostream>
#include <string>
using namespace std;
int n, f = 3, x, y, vx[4] = {-1, 0, 1, 0}, vy[4] = {0, -1, 0, 1};bool flg = 1;
int main()
{
cin >> n;
for(int i = 0;i < n;++i)
{
string s;cin >> s;
if(s == "right") f = (f == 0 ? 3 : f - 1);
else if(s == "left") f = (f + 1) % 4;
else
{
int t = stoi(s);
x += vx[f] * t, y += vy[f] * t;
cout << "(" << x << "," << y << ")" << endl;flg = 0;
}
}
if(flg) cout << "(0,0)";
return 0;
}
F
有一定思维难度。
光看思路长度也能看出这题放在水题比赛严重不合适啊
思路
第一部分
不难想到,需要先找到每个房子所对应的最佳现有灯。
根据题意,使灯功率 - 房子功率最小的灯是这个房子的最佳现有灯。
我们可以把房子所需亮度和灯的亮度放在一个数组里,然后把数组升序排序。
排序后,灯和房子越接近,对于那个房子来说灯就更好。(当然前提是灯在房子后面)
开一个栈,用来存放当前没有找到灯的房子。
然后把数组扫一遍:
- 对于房子,直接入栈
- 对于灯:
- 栈是空的:没有比这个灯更小的房子和这个灯匹配,这个灯没有用。
- 栈不是空的:栈顶是前面的没有匹配的房子中最大的,也就和这个灯最接近。所以栈顶和这个灯匹配,记录 灯功率 - 栈顶。
当然,有可能灯和房子功率一样,而灯排序后在房子前面,灯就浪费掉了。
所以在灯和房子功率一样时,把灯放在房子后面。
第二部分
上面说了,最佳现有灯要按灯功率 - 房子功率来比较,而非灯的功率。
所以上面记录的是灯功率 - 房子功率,而不是灯。
那么记录了灯功率 - 房子功率,怎么求灯总功率呢?
容易想到,先累加房子功率,再累加记录的灯功率 - 房子功率,就是灯总功率了。
(直接把灯的数组累加一遍不就是灯总功率了吗)
第三部分
然而我们还要更改 k 个灯。
那既然有一些灯没有用,就会有一些房子没有匹配灯。
那么这些房子就必须要单独买灯(现有的没法满足要求)。
有多少个房子呢?栈里剩下的房子就是没有匹配的。
-
所以栈的大小 > k,显然买不完,
NIE
。 -
如果栈的大小 ≤ k,可以买完,k -= 栈的大小。
显而易见,要买肯定买刚好的,所以这些房子的 灯功率 - 房子功率 = 0,累加时不用考虑它们。
如果还可以改呢?那就要改 灯功率 - 房子功率 较大的了。
刚才一直没有说把 灯功率 - 房子功率 记录在哪里,
这里为了方便,把它们扔进一个大根堆。
然后从堆顶往下改,直到 k 次用完为止。
显而易见,要买肯定买刚好的,所以这些房子的 灯功率 - 房子功率 = 0。
所以修改之后直接弹出即可,累加时不用考虑它们。
那还要考虑谁?仍然在大根堆里的。
把堆一个一个弹空,累加 灯功率 - 房子功率。
加上之前累加的房子功率的和,就是答案了。
代码
#include <iostream>
#include <utility>
#include <stack>
#include <queue>
#include <algorithm>
#define int long long
#define v first
#define d second
using namespace std;
int n, nd, k, ans;pair<int, bool> a[1000050];stack<int> s;priority_queue<int> q;
signed main()
{
cin >> n >> k;
for(int i = 0;i < 2 * n;++i)
{
cin >> a[i].v;
if(i < n) a[i].d = 1;
if(i >= n) ans += a[i].v;
}
sort(a, a + 2 * n);
for(int i = 0;i < 2 * n;++i)
{
if(!a[i].d) s.push(a[i].v);
else if(!s.empty()) q.push(a[i].v - s.top()), s.pop();
}
if(s.size() > k) {cout << "NIE";return 0;}
k -= s.size();
while(k--) q.pop();
while(!q.empty())
ans += q.top(), q.pop();
cout << ans;
return 0;
}
G
思路
没什么好说的。直接枚举每个花的颜色,判断是否符合要求。
复杂度 $O(4^nm)$,能过。
代码
#include <iostream>
#include <utility>
#define int long long
using namespace std;
int a[50], n, m, ans;pair<int, int> e[150];
void dfs(int x)
{
if(x > n)
{
for(int i = 0;i < m;++i)
if(a[e[i].first] == a[e[i].second]) return;
++ans;return;
}
for(int i = 1;i <= 4;++i)
a[x] = i, dfs(x + 1);
}
signed main()
{
cin >> n >> m;
for(int i = 0;i < m;++i)
cin >> e[i].first >> e[i].second;
dfs(1);
cout << ans;
return 0;
}