Codeforces Round #723 (Div. 2) A~C题解
A. Mean Inequality
-
题目大意
给你一个长度为 2 n 2n 2n的数组,重新编排顺序使得环状的 b i − 1 + b i + 1 2 ! = b i \frac{b_{i-1}+b_{i+1}}{2}!=b_i 2bi−1+bi+1!=bi。
-
解题思路
将数组从小到大排序,我们构造按大小大小的顺序依次构造即可,这很容易证明符合题目要求。
-
AC代码
/**
*@filename:A_Mean_Inequality
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-28 22:06
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5000 + 5;
const int P = 1e9+7;
int t,n,a[N];
bool vis[N];
void solve(){
}
int main(){
cin >> t;
while(t -- ){
cin >> n;
for(int i = 1; i <= 2 * n; ++ i){
cin >> a[i];
}
sort(a + 1,a + 2 * n + 1);
int i = 1,j = 2 * n;
while(i <= j){
printf("%d %d ",a[j],a[i]);
i ++ ;
j -- ;
}
printf("\n");
}
solve();
return 0;
}
B. I Hate 1111
-
题目大意
给你一个数 x x x,需要你判断 x x x是否能由 11 , 111 , 1111... 11,111,1111... 11,111,1111...的数组成。
-
解题思路
我们首先要知道一个定理,当正整数 A A A和 B B B互质的时候,**用A和B表示不出的最大数为A*B-A-B。**这里不予证明,所以我们发现,对于 11 11 11和 111 111 111互质,我们用这最小的两个构造就可以了,则最大不能表示的数为 1099 1099 1099。其余的都可以表示。那么对于小于这些的,我们可以利用完全背包解决,将 x x x看成背包体积。判断是否可达 x x x即可。当然,对于 < 1099 <1099 <1099的 x x x判断其实还有很多做法,例如暴力等。
这里我们推荐一个官方解法,我们先来证明一下:若 x = A ∗ 11 + B ∗ 111 x=A* 11+B*111 x=A∗11+B∗111,我们把 B B B表示为 C ∗ 11 + D , D < 11 C*11+D,D<11 C∗11+D,D<11。那么 x = ( A + C ∗ 111 ) ∗ 11 + D ∗ 111 x=(A+C*111)*11+D*111 x=(A+C∗111)∗11+D∗111,所以实际上我们只需要判断 x x x是否满足这个式子即可。
注意,两种解法都用到了开篇说明的定理,这可以有效的减小时间复杂度。
-
完全背包AC代码
/**
*@filename:B_I_Hate_1111
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-28 22:13
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;
int t;
ll x;
int main(){
cin >> t;
while(t -- ){
cin >> x;
if(x > 1099){
cout << "YES" << endl;
}
else{
bool flag = false;
while(true){
if(x % 11 == 0){
flag = true;
break;
}
x -= 111;
if(x < 0)break;
}
if(flag){
cout << "YES" << endl;
}
else{
cout << "NO" << endl;
}
}
}
return 0;
}
- 数学构造AC代码
/**
*@filename:B_I_Hate_1111
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-28 22:13
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;
int t;
ll x;
ll a[] = {0,11,111};
ll dp[N];
int main(){
cin >> t;
int n = 2;
while(t -- ){
cin >> x;
if(x > 1099){
cout << "YES" << endl;
}
else{
memset(dp,0,sizeof(dp));
dp[0] = 1;
for(int i = 1; i <= n; ++ i){
for(int j = 0; j <= x; ++ j){
if(j >= a[i]){
dp[j] = max(dp[j],dp[j - a[i]]);
}
}
}
if(dp[x] > 0){
cout << "YES" << endl;
}
else{
cout << "NO" << endl;
}
}
}
return 0;
}
C1,C2. Potions
-
题目大意
从左到右有 n n n瓶药水,每瓶药水都有健康值,你可以选择饮用或忽略。你不能使得你的健康值为负,初始值为 0 0 0,问能够饮用的最大药水数。
-
解题思路
-
01背包问题 O ( n 2 ) O(n^2) O(n2)
可以选或不选,这很明显就是我们熟悉的 01 01 01背包问题,我们定义 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i瓶药水饮用了 j j j瓶药水的最大能量值,易知该状态由 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]和 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1],需要注意的是,我们必须判断它们是否 ≥ 0 ≥0 ≥0,因为只有健康值 ≥ 0 ≥0 ≥0才可以继续饮用,至此,状态转移方程易得: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − 1 ] + a [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+a[i]) dp[i][j]=max(dp[i−1][j],dp[i−1][j−1]+a[i])。
该做法时间复杂度为 O ( n 2 ) O(n^2) O(n2),只能过 C 1 C1 C1。
-
反悔贪心,优先队列维护选择的负值 O ( n l o g n ) O(nlogn) O(nlogn)
我们发现,在选择的过程中,正数是一定可以选择的,而负数则不一定,如果我们当前有能量值 7 7 7,而之前选择了 − 8 -8 −8,而后有 − 3 , − 6 -3,-6 −3,−6。我们显然可以不选 − 8 -8 −8,转而选择 − 3 -3 −3和 − 6 -6 −6,这也就是反悔贪心,我们当前有更优的策略,就可以更改之前的错误决定。
既然这样,那么我们如何实现这种贪心呢?实际上我们只需要优先队列维护已经选择的负值,然后在每次加上负值不符合条件后判断是否有更优的负值供替换。这种思想就是当前已经为负值,即代表不选择此时的值,而若之前有比这更小的负数,我们为何不替换掉呢? 这种贪心策略显然可以得到全局最优解。 O ( n l o g n ) O(nlogn) O(nlogn)的做法是优秀的,可以过 C 2 C2 C2
-
-
01背包AC代码
/**
*@filename:C1_Potions_Easy_Version_
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-28 22:42
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2000 + 5;
const int P = 1e9+7;
int n,a[N];
ll dp[N][N];//dp[i][j][0]表示前i瓶已经喝了j瓶,此时你的体力。如果为负数则淘汰。
void solve(){
fill(dp[0],dp[0] + N * N,-0x3f3f3f3f);
for(int i = 0; i <= n; ++ i){
dp[i][0] = 0;
}
//状态转移,dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - 1] + a[i])
for(int i = 1; i <= n; ++ i){
for(int j = 1;j <= i; ++ j){
if(dp[i - 1][j - 1] >= 0){
dp[i][j] = max(dp[i - 1][j - 1] + a[i],dp[i][j]);
}
if(dp[i - 1][j] >= 0){
dp[i][j] = max(dp[i - 1][j],dp[i][j]);
}
}
}
for(int j = n; j >= 0; -- j){
if(dp[n][j] >= 0){
cout << j <<endl;
break;
}
}
}
int main(){
cin >> n;
for(int i = 1; i <= n; ++i){
cin >> a[i];
}
solve();
return 0;
}
- 反悔贪心AC代码
/**
*@filename:C1_Potions_Easy_Version_
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-28 22:42
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200000 + 5;
const int P = 1e9+7;
int n,a[N];
priority_queue<int,vector<int>,greater<int> > q;
void solve(){
int res = 0;
ll ans = 0;
for(int i = 1; i <= n; ++ i){
if(ans + a[i] < 0){
if(q.empty() || a[i] <= q.top()){
continue;
}
else{
ans = ans - q.top() + a[i];
q.pop();
q.push(a[i]);
}
}
else{
res ++;
ans += a[i];
if(a[i] < 0){
q.push(a[i]);
}
}
}
printf("%d\n",res);
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf("%d", &a[i]);
}
solve();
return 0;
}

浙公网安备 33010602011771号