Codeforces Round #642 (Div. 3)

58 篇文章 0 订阅
13 篇文章 1 订阅

A. Most Unstable Array

题意:给一个n和m,要建一个n元素的数组,使得数组和加起来等于m。然后相邻元素的差的总和最大。数组元素不小于0。

思路:显然,使得绝对值最大的就是0,m,0,0,0,0…。所以绝对值之和就是2*m。直接冲。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int t,n,m,k;
int a[N];
 
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n>>m;
        n --;
        if(n == 0){
            cout<<0<<endl;
            continue;
        }
        if(n == 1){
            cout<<m<<endl;
        }
        if(n > 1){
            cout<<2*m<<endl;
        }
    }
    return 0;
}

B. Two Arrays And Swaps

题意:两个数组,可以把数组b的元素和数组a的交换,可以交换最多x个。

思路:显然,用b最大的替换a最小的。如果b中的还没有a中的大那就不用换了。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int t,n,m,k;
int a[N],b[N];
 
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i = 0 ; i  < n ; i ++) cin>>a[i];
        for(int i = 0 ; i  < n ; i ++) cin>>b[i];
        int sum = 0;
        for(int i = 0 ; i < n ; i ++ ) sum += a[i];
        sort(a,a+n);
        sort(b,b+n);
        int pos = 0;
        for(int i = n-1 ; i >= 0 && m;pos ++, m--, i --){
            if(b[i] > a[pos]){
                sum += b[i]-a[pos];
            }
        }
        cout<<sum<<endl;
    }
    return 0;
}

C. Board Moves

题意:n*n的格子,要把所有格子全部移到一个某格子里面。可以往八个方向移动。问最少需要移动多少次。

思路:因为n是奇数。所以n*n的方格一定存在一个中心。中心到周围一圈就是1步可以移动的范围。然后再往外依次+1,格子个数也依次+8。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int t,n,m,k;
int a[N],b[N];
 
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n;
        int cnt = 8;
        int sum = 0;
        int tmp = 1;
        for(int i = 3; i <= n ; i += 2){
            sum += cnt*tmp;
            cnt += 8;
            tmp ++;
        }
        cout<<sum<<endl;
    }
    return 0;
}

D. Constructing the Array

题意:初始一个全0的数组,然后把区间长度最长的中点赋值为1,然后剩下的区间继续按长度排序,相同长度按左端点排序。

思路:用优先队列暴力模拟。类似二分的递归应该也可以,但是没想怎么写。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int t,n,m,k;
int a[N],b[N];
struct node{
    int l,r;
    int g;
    node(int a,int b){
        l = a;
        r = b;
        g = b-a+1;
    }
    friend bool operator <(const node &a1,const node &a2){
        if(a1.g == a2.g){
            return a1.l > a2.l;
        }
        return a1.g < a2.g;
    }
};
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i = 1 ; i <= n ; i ++) a[i] = 0;
        priority_queue<node> que;
        que.push(node(1,n));
        int i = 1;
        while(!que.empty()){
            node now = que.top();
            que.pop();
            int mid = (now.l+now.r)/2;
            if(a[mid]) continue;
        //cout<<mid<<" "<<i<<" "<<now.l<<" "<<now.r<<endl;
 
            a[mid] = i++;
            if(now.g > 1){
                que.push(node(now.l,mid-1));
                que.push(node(mid+1,now.r));
            }
        }
        for(int j = 1 ; j <= n ; j ++){
            cout<<a[j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

E. K-periodic Garland

题意:一个01串,要求每两个相邻的1之间的距离正好等于k。现在问这个串最少要改多少次,可以符合题目要求。

思路:串的最前面可以很多0,最后面也可以很多0。只有中间需要100010001这样隔开就行。然后考虑用dp。dp[i][0]表示前i个元素合法,并且第i个位置为0,dp[i][1]同理。那么对于0,只要前面是合法的,这个位置放0就是合法的。因为后面可以全为0嘛,如果后面没有1的话。所以放0是没有什么条件限制的。但是放1的话,就要考虑前面的状态了。要么前面全是0。要么前面i-k的位置上1,并且要把这两个1之间的所有1都改成0。所以要计算这个代价,还需要求一个前缀和,用来求 i-k ~ i之间的1的个数。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
//const int N = 1e6+7;
const int N = 1e6 + 10;
const int mod = 998244353;
int t,n,m,k;
int a[N],b[N];
int res[N];
int cnt[N];
int dp[N][2];
string s;
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n>>k;
        cin>>s;
        for(int i = 1 ; i <= n ; i ++){
            dp[i][0] = dp[i][1] = 0;
            cnt[i] = cnt[i-1] + s[i-1] -'0';
        }
        for(int i = 1 ; i <= n ; i ++){
            int p = max(0LL,i-k);
            dp[i][0] = min(dp[i-1][0],dp[i-1][1])+s[i-1]-'0';
            dp[i][1] = min(cnt[i-1],dp[p][1]+cnt[i-1]-cnt[p])+(s[i-1] == '0');
        }
        cout<<min(dp[n][0],dp[n][1])<<endl;
    }
    return 0;
}

F. Decreasing Heights

题意:一个n*m的网格。起点在(0,0),终点(n,m),然后只能往下或者往右走。并且,要走的格子只能比当前格子大 1。现在可以改变这个网格的值,每次只能对一个数 -1,问最少进行多少次操作,可以从起点走到终点。题目保证有解。

思路:首先考虑起点固定一个值就和普通的走方格一样了,那么就可以o(n2)求出当前起点值下的最优解。然后尝试改变起点的值。每一次都要n2求,那显然时间复杂度不可接受。那么还要一个点需要想到。就是,最优解中,一定是有一个方格中的值是不需要减的。如果所有方格都-1了,那么不减1不是也一样可以走到嘛。所以,暴力枚举 方格中的所有出现的值,然后对每个值,n2 dp,就完事了。可惜重现的时候还是想不到,太久没认真训练了,实力直线下坠。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
const int mod = 998244353;
int t,n,m,k;
int a[105][105];
int dp[105][105];
int res[N];
string s;
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i = 0; i < n ; i ++){
            for(int j = 0 ; j < m ; j ++){
                cin>>a[i][j];
            }
        }
        int pre = a[0][0];
        int res = 1e18;
        for(int i = 0 ; i < n ; i ++){
            for(int j = 0 ; j < m ; j ++){
                int now = a[i][j]-i-j;
                if(now > pre){continue;}
                for(int x = 0 ; x < n ; x ++){
                    for(int y = 0 ; y < m ; y ++){
                        dp[x][y] = 1e18;
                    }
                }
                dp[0][0] = pre-now;
                for(int x = 0 ; x < n ; x ++){
                    for(int y = 0 ; y < m ; y ++){
                        int noww = now+x+y;
                        if(a[x][y] >= noww){
                            int cnt = a[x][y] - noww;
                            if(x > 0) dp[x][y] = min(dp[x][y],dp[x-1][y]+cnt);
                            if(y > 0) dp[x][y] = min(dp[x][y],dp[x][y-1]+cnt);
                        }
                    }
                }
                res = min(res,dp[n-1][m-1]);
            }
        }
        cout<<res<<endl;
 
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值