cf #821 Div.2(A~E)

Cf #821 Div.2

A. Consecutive Sum

  • 题意

    • 给定一个长度为n的数组以及k(1<=k<100),下标模余k相同的数可以交换
    • 通过上述操作找出长度为k的连续数之和的最大值
  • 题解

    • 由题意得,下标mod k相同的为一个可选集合,分别找出k个集合中的最大值,相加即为答案
  • 代码

改进:用二维vector来存下标余数一样的数,代码更加简洁,不需要跨k个k个的找

#include <iostream>
#include <algorithm>

using namespace std;
const int N=110;

int a[N];

void solve() {
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    
    long long ans=0;
    for(int i=1;i<=k;i++) {
        int m=0;
        for(int j=i;j<=n;j+=k) m=max(m,a[j]);
        ans+=m;
    }
    cout<<ans<<'\n';
}

int main() {
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

B. Rule of League

  • 题意

    • 有n个人比赛,每一场两个人比,输了的直接输掉,赢得继续打下一个人,第一场1vs2,第二场第一场赢的人vs 3,以此类推总共打n-1场
    • 现在给n,x,y,所有人赢的次数不是x就是y,输出n-1场赢的人的编号
  • 题解

    • 由题得,只要一开始就输的人直接输了,即没有赢的机会,所以x,y中肯定有一个为0,而另一个肯定是n-1的因数,假设x=0,因为赢过的人都要赢y次,总共有n-1场,所以y一定是n-1的因数
    • 所以可以得到结论,只有一个为0另一个为n-1的因数才有解。有解的情况,有n-1/y个人赢过y次,且可以直接输出对应场的其中一个人
  • 代码

#include <iostream>
#include <algorithm>

using namespace std;
const int N=1e5+10;

void solve() {
    int n,x,y;
    cin>>n>>x>>y;
    bool ok=false;
    if(x==0&&y&&((n-1)%y==0)) ok=1;
    if(y==0&&x&&((n-1)%x==0)) ok=1;
    if(!ok) {cout<<-1<<'\n';return ;}
    
    int t=(x==0 ? y:x);
    for(int i=1;i<=n-1;i++) {
        int j=0;int f=0;
        while(j<t) { cout<<i+1<<' '; j++;f=1;}
        if(f) j--;
        i+=j;
    }
    puts("");
}

int main() {
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

C. Parity Shuffle Sorting

  • 题意

    • 给定一个n的数组,通过下列操作使得整个数组变成不递减的
    • 选择两个下标,al+ar为奇数则ar=al,al+ar为偶数则al=ar
  • 题解

    • 因为选择后,要么左端点变右端点,要么右端点变左端点,贪心一下,直接让数组开头和结尾都变成一样的,那么中间没个数的每一次变化都可以选择左端点或者右端点,让数组变成一个完全相等的数组
  • 代码

#include <iostream>
#include <algorithm>

using namespace std;
const int N=1e5+10;

int a[N];

void solve() {
    int n;
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    if(n==1){cout<<0<<'\n';return;}//特判一个数的时候
    cout<<n-1<<'\n';//全部都变成一样的,所以要n-1次
    
    int s=a[0]&1,e=a[n-1]&1;
    if(s!=e) {//首位奇偶不同
        cout<<1<<' '<<n<<'\n';
        int t=a[0];//那么数组最终变成首位数
        for(int i=1;i<n-1;i++) {
            if((a[i]+t)&1) cout<<1<<' '<<i+1<<'\n';
            else cout<<i+1<<' '<<n<<'\n';
        }
    }
    else {//奇偶相同
        cout<<1<<' '<<n<<'\n';
        int t=a[n-1];//数组变为尾数
        for(int i=1;i<n-1;i++) {
            if((a[i]+t)&1) cout<<1<<' '<<i+1<<'\n';
            else cout<<i+1<<' '<<n<<'\n';
        }
    }
}

int main() {
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

D1. Zero-One (Easy Version)

  • 题意

    • 给两个长度为n的字符串a,b,进行以下两个操作,问将两个字符串变成一样的最小代价
    • 操作:选择两个下标l,r进行01互换,若l,r相邻那么操作代价为x,否则代价为y,且x>y
  • 题解

    • 易得,只有当不同的位置数量是偶数才能变成一样
    • 当不同位置的数量为2个时,可以采用两种操作,一是直接用代价为x的操作,另一种是借用另一个数把相邻的变成不相邻的,使用两次代价为y的操作。取其中代价小的
    • 当不同数量>2,那么都可以直接用y代价的操作改变
  • 代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
#define int long long

void solve() {
    int n,x,y;
    cin>>n>>x>>y;
    string a,b;
    cin>>a>>b;
    
    int cnt=0,id,f=0;
    for(int i=0;i<n;i++) {
        if(a[i]!=b[i]) {
            cnt++;
            if(!f) f=1,id=i;//记录第一个不同的位置,如果不同数量为2,那么id起作用
        }
    }
    
    if(cnt&1) { puts("-1"); return;}
    if(cnt==2&&a[id+1]!=b[id+1]) {cout<<min(x,2*y)<<'\n'; return;}
    cout<<cnt/2*y<<'\n';
}

signed main() {
    int t;
    cin>>t;
    while(t--) solve();
    return 0;
}

D2. Zero-One (Hard Version)

  • 题意

    • 给两个长度为n的字符串a,b,进行以下两个操作,问将两个字符串变成一样的最小代价
    • 操作:选择两个下标l,r进行01互换,若l,r相邻那么操作代价为x,否则代价为y
  • 题解

    • 由上一题提示,可以把问题分解成x>=y以及x<y两部分,前者用贪心,后者用dp

    • x<y,贪一下,两个分隔的可以借助中间的去用操作1,也就是说都看成使用操作1,那么距离越远的两个位置代价越大,那么最远的两个大概可以使用代价y的操作2

    • dp定义:f[i,j]表示对于区间段i~j的最小代价

      转移:对于某个段,可以选择头两个,尾部两个,或者一头一尾各一个

  • 代码

#include <iostream>

using namespace std;
const int N=5005;

int n,T,tot,pos[N];//pos记录不同的位置下标
string a,b;
long long x,y,f[N][N];

long long dfs(int l,int r) {//记忆化搜索的dp
    if(l>r) return 0;
    if(~f[l][r]) return f[l][r];
    
    long long ans;
    ans=min(dfs(l,r-2)+min(y,x*(pos[r]-pos[r-1])) , dfs(l+2,r)+min(y,x*(pos[l+1]-pos[l])));
    ans=min(ans,dfs(l+1,r-1)+y);
    return f[l][r]=ans;
}

int main() {
    cin>>T;
    while(T--) {
        cin>>n>>x>>y;
        cin>>a>>b;
        
        tot=0;
        for(int i=0;i<n;i++) {
            if(a[i]!=b[i]) 
                pos[++tot]=i+1;
        }
        
        if(tot&1) { puts("-1"); continue; }
        if(x>=y) {
            if(tot==2&&(pos[1]+1==pos[2])) { cout<<min(x,2*y)<<'\n'; continue; }
            cout<<1ll*tot/2*y<<'\n';
        }
        else {
            for(int i=1;i<=tot;i++) 
                for(int j=1;j<=tot;j++) 
                    f[i][j]=-1;
                    
            cout<<dfs(1,tot)<<'\n';
        }
    }
    return 0;
}

E. Conveyor

  • 题意

    • 有120*120的矩阵,矩阵中每个格子都有一个初始为向右的传送带,每一秒都会在(0,0)上放一个箱子
    • 传送带当传送了一个箱子,其方向会改变,改变规则为向右->向下,向下->向右
    • 问t秒时,在(x,y)上是否有箱子
  • 题解

    • 如果直接模拟t秒,看(x,y)上是否有箱子,太困难了。转变思路看t秒内,有几个箱子经过了(x,y)点,那么get(t,x,y)-get(t-1,x,y)就是t秒时经过(x,y)的箱子数量
    • 如何计算t秒内经过某点的箱子数量,首先t秒内可能走到(x,y)的数量是t-(x+y)+1个,(x,y)的数量会向右或者下传送,a[x+1,y]数量为a[x,y]/2,而a[x,y+1]数量为a[x,y]-a[x,y]/2(上取整)
  • 代码

#include <iostream>
#include <cstring>

using namespace std;
long long a[121][121];

long long get(long long t,long long x,long long y) {//算出t秒内经过(x,y)的箱子数量
    memset(a,0,sizeof a);
    a[0][0]=t-x-y+1;
    if(a[0][0]<0) a[0][0]=0;
    for(int i=0;i<=x;i++) 
        for(int j=0;j<=y;j++)
            a[i+1][j]+=a[i][j]/2,
            a[i][j+1]+=a[i][j]-a[i][j]/2;
            
    return a[x][y];
}

void solve() {
    long long t,x,y;
    cin>>t>>x>>y;
    cout<<(get(t,x,y)-get(t-1,x,y)>0 ? "YES":"NO")<<'\n';//有箱子输出YES
}

int main() {
    int q;
    cin>>q;
    while(q--) solve();
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<div id="wea_rich_text_default_font" style="font-family:微软雅黑;font-size:12;"><p><img alt="" src="/weaver/weaver.file.FileDownload?fileid=aaa9aee4717d33272bd7ea028fa03118b693919f23b18febf9f6cee1158e8f4cf027542c71c8cf98d63770ccdf3bd1750e6b92e28c43dccd4" /></p><div class="ckeditor-html5-video" data-widget="html5video" style="text-align:left"><video controls="controls" src="/weaver/weaver.file.FileDownload?fileid=aad6f413f83191673980c5ee24b412880d6b9e8703caca411faec3276fe8133f5fa7e34630ca89ace63770ccdf3bd175071362141037cfb4e&download=1" style="max-width:100%"> </video></div><table border="1" cellpadding="1" style="width:500px;"> <tbody> <tr> <td style="padding: 1px;">1</td> <td style="padding: 1px;">1</td> </tr> <tr> <td style="padding: 1px;">2</td> <td style="padding: 1px;">2</td> </tr> <tr> <td style="padding: 1px;">3</td> <td style="padding: 1px;">3<a href="http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830" target="_blank">http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830</a></td> </tr> </tbody></table><p>测试<a href="http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830" target="_blank">http://localhost:8080/wui/index.html#/main/portal/portal-1-1?menuIds=0,1&menuPathIds=0,1&_key=zq8830</a></p><p> </p><p>修改一下吧 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq<img alt="" src="/weaver/weaver.file.FileDownload?fileid=a7617945ec5f52ec80aaa43ee8504de0a1b14d5eca4a98834494c85349762c626dec7ba8d0da277106ee600d27743f4e44f710fbddd167603" /></p></div>
06-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值