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 2bi1+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=A11+B111,我们把 B B B表示为 C ∗ 11 + D , D < 11 C*11+D,D<11 C11+D,D<11。那么 x = ( A + C ∗ 111 ) ∗ 11 + D ∗ 111 x=(A+C*111)*11+D*111 x=(A+C111)11+D111,所以实际上我们只需要判断 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[i1][j] d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1],需要注意的是,我们必须判断它们是否 ≥ 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[i1][j],dp[i1][j1]+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;
}
posted @ 2022-03-26 16:48  unique_pursuit  阅读(44)  评论(0)    收藏  举报