1003考试
1003考试
T1
题目大意:
一支蜡烛可以燃烧一个小时,然后就会熄灭。如果你拥有𝑏支已经熄灭的蜡烛,你可以将它们做成一支新的蜡烛。最开始你有𝑎支蜡烛,你最多能让它们燃烧多久呢?
太水了太水了,直接切。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
long long a, b, ans, res, sum;
int main() {
a = read(); b = read(); sum = a;
ans = a;
for(int i = 1; ; i++) {
res = sum / b; ans += res;
sum = res + sum % b;
if(sum < b) break;
}
printf("%lld", ans);
fclose(stdin); fclose(stdout);
return 0;
}
T2
题目大意:
现有𝑛支队伍进行循环赛(每两支队伍恰好比赛一场),胜者得3分,平局各得1分,负者不得分。给出这𝑛支队伍的最终总得分,求满足条件的方案数。(网上可以搜到原题)
搜索 + 记忆化 + 剪枝。
缩索的框架呢,就是一个格一个格的往里面填数,显然要填\(n *(n - 1) / 2\)个格子。
可行性剪枝:判断当前行的sum如果大于这个球队的分数,直接返回;如果当前行的sum加上后面几场全部赢的分数还不能符合条件,那么也直接返回。我们还可以根据已有信息算出整个里面有几个胜场,几个平局,我们在搜的时候如果胜场或平局的个数大于这个,那么也直接返回。
记忆化:当前行的填数方案确定了之后,我们把剩下几行搞个哈希,下次搜到相同的直接返回本次算出的值就好了。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 11, mod = 1e9 + 7;
int n, win, pin, sum;
int a[N], b[N], sum_hang[N];
map <long long, long long> mp;
int cmp(int a, int b) { return a > b; }
int dfs(int x, int y) {
if(x == n) return 1;
if(sum_hang[x] + (n - y + 1) * 3 < a[x]) return 0;
if(y == n + 1) {
for(int i = x + 1;i <= n; i++) b[i] = a[i] - sum_hang[i];
sort(b + x + 1, b + n + 1);
long long res = 0;
for(int i = x + 1;i <= n; i++) res = 1ll * res * 28 + b[i];
if(mp.find(res) != mp.end()) return mp[res];
else return mp[res] = dfs(x + 1, x + 2);
}
long long sum = 0;
if(sum_hang[x] + 3 <= a[x] && win) { //赢
sum_hang[x] += 3; win --;
sum += dfs(x, y + 1);
sum_hang[x] -= 3; win ++;
}
if(sum_hang[y] + 3 <= a[y] && win) { //输
sum_hang[y] += 3; win --;
sum += dfs(x, y + 1);
sum_hang[y] -= 3; win ++;
}
if(sum_hang[x] + 1 <= a[x] && sum_hang[y] + 1 <= a[y] && pin) { //平局
sum_hang[x] ++, sum_hang[y] ++; pin --;
sum += dfs(x, y + 1);
sum_hang[x] --, sum_hang[y] --; pin ++;
}
return sum % mod;
}
int main() {
n = read();
for(int i = 1;i <= n; i++) a[i] = read(), sum += a[i];
win = sum - n * (n - 1);
pin = n * (n - 1) / 2 - win;
sort(a + 1, a + n + 1, cmp);
printf("%d", dfs(1, 2) % mod);
fclose(stdin); fclose(stdout);
return 0;
}
T3
题目大意:
有𝑛个人想要坐车,线路可以抽象成一条数轴。第𝑖个人想要从坐标𝑠𝑖坐到坐标𝑡𝑖。你的车从原点0出发,最终行驶到坐标𝑚。车上最多只能同时坐一个乘客,但你可以让乘客中途下车,只需保证最终将其送达他的目的地即可。在满足所有人的需求下,你行驶的最小总路程是多少呢?
这个题难死我了,暴力也不会打。
我们把车走过的路程分为两部分,一部分是车上有人,一部分是没人。
车上有人的很好求:\(\displaystyle \sum_{i = 1}^{n} abs(t[i] - s[i])\);
现在我们要最小化车上没人的路程,当我们运送完某一个人时,我们找到一个最近的上车点肯定是最优的,因为如果有一个稍远一点的上车点,那还不如先把最近的这个人载一乘。(貌似这么解释会少一点情况,但就是这么贪心的走,路程就最小)
所以现在只需把所以的\(s[i], t[i]\)排个序就好了,注意让\(s[0] = m, t[0] = 0\), 因为车要从0走到m。
正解代码还是很少的:
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5;
int n, m;
int s[N], t[N];
long long ans;
int cmp(int a, int b) { return a < b; }
int main() {
n = read(); m = read();
for(int i = 1;i <= n; i++) s[i] = read(), t[i] = read(), ans += abs(t[i] - s[i]);
s[0] = m, t[0] = 0;
sort(s, s + n + 1, cmp);
sort(t, t + n + 1, cmp);
for(int i = 0;i <= n; i++) ans += abs(s[i] - t[i]);
printf("%lld", ans);
fclose(stdin); fclose(stdout);
return 0;
}
T4
题目大意:
你参加了一次比赛,共有𝑛(奇数)个评委给你打分,编号为1到𝑛。你最终的得分通过如下方式得到:评委们按编号排成一排,每次取出前三名评委,留下其中评分的中位数并将其加入队尾,直到只剩最后一名评委,该评委的评分就是你最终的得分。现在你知道了所有评委的评分和部分评委的编号,你想要知道,对于剩余评委所有可能的编号情况中,你的得分最高能达到多少。
二分答案。
我们二分中位数的值,显然答案具有单调性。
我们把原序列大于等于\(mid\)数赋为1,小于的赋为0。那么现在有一些位置不确定,但是我们可以知道这些位置总共1的个数是多少, 设这个个数为\(res\)。
设\(f[i]\)表示\(i\)这个位置变成1的最小代价。代价指什么呢?代价就是在之前不确定的位置上需要放几个1,也就是放几个大于等于\(mid\)的数,这个位置\(i\)才能变成1。那么对于原序列中的1,\(f[i] = 0\),原序列中的0,\(f[i] = inf\)。我们想让最后取出的中位数大于等于\(mid\),就得求出\(f[t]\),\(t\)是中位数的位置,如果\(f[t] <= res\),那么这个\(mid\)就符合条件。
然后我们按题意模拟,把队列里前三个取出。只有当这前三个数有两个1的数时,当前这三个的中位数才为1,所以我们要取其中两个最小的\(f\)值,把它放到后面就好了。至于一定会有两个0的情况,那说明这个中位数一定为0,相同的操作把它加到后面就好了,不会有影响。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 2e5 + 5;
const long long inf = 2e8;
int n, m, t, ans;
int a[N], b[N], f[N];
int judge(int mid) {
int res = 0;
for(int i = m + 1;i <= n; i++) res += (b[i] >= mid);
for(int i = 1;i <= n; i++)
f[i] = a[i] ? (a[i] >= mid ? 0 : inf) : 1;
queue <int> q;
for(int i = 1;i <= n; i++) q.push(f[i]);
while(!q.empty()) {
int x = q.front(); q.pop();
if(q.empty()) return x <= res;
int y = q.front(); q.pop();
int z = q.front(); q.pop();
q.push(min(min(x + y, x + z), y + z));
}
}
signed main() {
int l = inf, r = 0;
n = read(); m = read();
for(int i = 1, x, y;i <= m; i++) b[i] = read(), x = read(), a[x] = b[i], l = min(l, b[i]), r = max(r, b[i]);
for(int i = m + 1;i <= n; i++) b[i] = read(), l = min(l, b[i]), r = max(r, b[i]);
while(l <= r) {
int mid = (l + r) >> 1;
if(judge(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%d", ans);
fclose(stdin); fclose(stdout);
return 0;
}
总结
预计得分:100 + 100 + 50 = 250pts; 实际得分: 100 + 90 + 40 = 230pts。
第三题爆零了,暴力不会打,这也太菜了。还是做题少吧,毕竟当时一点思路也没有。
第二题我只想到两个,记忆化也没想到,还是多做题吧,以后应该就能想到了。