【学校训练记录】12月个人训练赛1个人题解
A

对于n本书拿出k本较为难实现,但是从n本书里拿出n-k本就容易多了
对于n本书里拿一本为特殊情况,不管怎么拿都为0
对于n本书里拿n-k本的话,我们假设拿的最后一本为i那么
他就是拿出n-k-1本书的情况再加上拿出第i本的情况
其中差值变化为拿出n-k-1本书的值,加上我abs(w[i]-w[j])(j为拿出n-k-1本书情况的最后面的那本书)
将f[i][j]视为从i本书里拿出j本的最小差值
于是可以得到转移方程f[i][j] = min(f[i][j],f[l][j]+abs(w[i]-w[j]))
其中l为拿出j-1到i-1本书的情况
#include <bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
struct sss{
int l, r;
}w[110];
int n, k, f[110][110];
bool cmp(sss w1, sss w2){
return w1.l<w2.l;
}
void solve(){
cin >> n >> k;
k = n-k;
for(int i = 1; i <= n; i++) cin >> w[i].l >> w[i].r;
for(int i = 1; i <= n; i++)
for(int j = 1;j <= n; j++)
f[i][j] = 1e18;
sort(w+1,w+1+n,cmp);
for(int i = 1; i <= n; i++) f[i][1] = 0;
for(int i = 2; i <= n; i++){
for(int j = 2; j <= min(i,k); j++)
for(int l = j-1; l < i; l++)
f[i][j]=min(f[i][j],f[l][j-1]+abs(w[i].r-w[l].r));
}
int mins = 1e18;
for(int i = k; i<=n; i++){
mins = min(mins, f[i][k]);
}
cout << mins;
}
signed main (){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
B

找出最大的两个值,与最大值相等就输出第二大的值,否则输出第一大的值
#include <bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
int n, a[200010], b[200010];
void solve(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
b[i] = a[i];
}
sort(b+1,b+1+n);
for(int i = 1; i <= n; i++){
if(a[i] == b[n]) cout << b[n-1] << endl;
else cout << b[n] <<endl;
}
}
signed main (){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
C

考虑拿两种技能都能有的情况的值,第一个技能的最小花费和第二个技能的最小花费的值的和,输出他们的最小值即可。
#define endl "\n"
#define int long long
using namespace std;
int n;
struct aaa{
int num;
string ch;
}w[200010];
bool cmp(aaa w1, aaa w2){
if(w1.ch!=w2.ch){
return w1.ch>w2.ch;
}
return w1.num<w2.num;
}
void solve(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> w[i].num >> w[i].ch;
}
sort(w+1,w+1+n,cmp);
int mins = 1e18;
int j;
for(j = 1; j <= n; j++){
if(w[j].ch=="11"){
mins = min(mins, w[j].num);
}
else break;
}
int ret = 1e18, p = 0;
for(; j <= n; j++){
if(w[j].ch=="10"){
ret = min(ret, w[j].num);
p = 1;
}
else break;
}
for(; j <= n; j++){
if(w[j].ch == "01"){
ret+=w[j].num;
p = 2;
break;
}
else break;
}
if(p==2)mins = min(mins, ret);
if(mins == 1e18) cout << "-1" << endl;
else cout << mins << endl;
}
signed main (){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
D

要求不重复的正整数,那就从1开始给,直到n值不允许再额外给出比他大1的值时,直接给出剩下的全部
#define endl "\n"
#define int long long
using namespace std;
int n, a[1100];
void solve(){
cin >> n;
int num = 0;
for(int i = 1; i <= n; i++){
if(n>=i+i+1){
a[i] = i;
num++;
n-=i;
}
else{
a[i] = n;
num++;
break;
}
}
cout << num << endl;
for(int i = 1; i <= num; i++){
cout << a[i];
if(i!=num) cout << ' ';
}
}
signed main (){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
E

根据题意可以得到f(i)<=g(i)是一定成立的,因为比他大的数的数量加一 跟 比他大的数的数量加上自身是一样大的,若再加上其他与自身相等的数就是大于的情况了
那剩下需要考虑的就只有k的情况
那么对于ai刚好等于ak的情况,我们就不需要操作。
对于ai大于ak的情况,即k可能大于g(i)的情况,就需要把比他小的值到ak的值全部变为自身即可。(此处需要前缀和来维护)
对于ai小于ak的情况,即k可能小于f(i)的情况,就需要把ai变成ak即可
#define endl "\n"
#define int long long
using namespace std;
struct sss{
int num;
int p;
}w[500010];
int n, k;
bool cmp(sss w1, sss w2){
return w1.num>w2.num;
}
int sum[500010], ret[500010];
void solve(){
int xxxx;cin >> xxxx >> n >> k;
for(int i = 1; i <= n; i++){
cin >> w[i].num;
w[i].p = i;
}
sort(w+1,w+1+n, cmp);
for(int i = 1; i <= n; i++) sum[i] = sum[i-1]+w[i].num;
for(int i = 1; i <= n; i++){
if(w[i].num == w[k].num) ret[w[i].p] = 0;
else if(w[i].num > w[k].num){
ret[w[i].p] = w[i].num*(k-i)-sum[k]+sum[i];
// cout << w[i].num << ' ' << k << ' ' << i << ' ' << sum[k] << ' ' << sum[i] << endl;
}
else if(w[i].num < w[k].num) ret[w[i].p] = w[k].num-w[i].num;
}
for(int i = 1; i <= n; i++){
cout << ret[i] << endl;
}
}
signed main (){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}```

浙公网安备 33010602011771号