暑假的第一次测试(一)

本文解析了Codeforces平台上的两个经典编程题:一是通过字符串匹配寻找最大数量的不重叠子串;二是利用二分加贪心算法解决搬运箱子问题,以达到最短时间内的最优解。

A题 ZgukistringZ( 原题 codeforces Round # 307 Div.2 B)

一般的字符串模拟题。题目的大致意思是有字符串a、b、c。将a的某些字母进行交换,问a的不重叠子串和b、c相同的最多次数。

比如样例三:

a串:abbbaaccca

b串:ab

c串:aca

将a变成ababacabcc,相同的有ab、ab、aca三个不重叠子串,注意题目说明是不重叠。

然后我觉得是一个模拟题,将a串中的所有字母进行个数统计,b、c串的也分别进行统计。然后可以先算出如果用a串只去构成b串最多有多少串,设为x;之后就枚举构成b串的数目,将a串的字母个数减去b串的对应字母个数*b串的个数,剩下的字母能构成多少个c串,然后求个max并记录此时的b串和c串数目。

之后只要输出b串、c串,最后再输出剩余的字母。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int min(int a,int b)
{
    if (a<b) return a; else return b;
}
int la,lb,lc,i,j,x,num,num2,d1,d2,maxn;
char sa[100012],sb[100012],sc[100012];
int pa[26],pb[26],pc[26],b[26];
int main()
{
    scanf("%s",sa); la=strlen(sa);
    scanf("%s",sb); lb=strlen(sb);
    scanf("%s",sc); lc=strlen(sc);
    memset(pa,0,sizeof(pa));
    memset(pb,0,sizeof(pb));
    memset(pc,0,sizeof(pc));
    for (i=0;i<la;i++)
        pa[sa[i]-'a']++;

    for (i=0;i<lb;i++)
        pb[sb[i]-'a']++;

    for (i=0;i<lc;i++)
        pc[sc[i]-'a']++;
    num=-1;
    for (i=0;i<=25;i++)
    if (pb[i]!=0)
    {
       if (num!=-1) num=min(num,pa[i]/pb[i]);
       else num=pa[i]/pb[i];
    }
    for (i=0;i<=num;i++)
    {
        for (j=0;j<=25;j++)
            b[j]=pa[j]-pb[j]*i;
        num2=-1;
        for (j=0;j<=25;j++)
            if (pc[j]!=0)
            if (num2!=-1) num2=min(num2,b[j]/pc[j]);
            else num2=b[j]/pc[j];
        if (i+num2>maxn)
        {
            maxn=i+num2;
            d1=i; d2=num2;
        }
    }
    for (i=1;i<=d1;i++)
    cout<<sb;
    for (i=1;i<=d2;i++)
    cout<<sc;
    for (i=0;i<=25;i++)
    {
        x=pa[i]-(pb[i]*d1+pc[i]*d2);
        for (j=1;j<=x;j++)
            cout<<char(97+i);
    }
    cout<<endl;
    return 0;
}

B题 GukiZ hates Boxes(原题codeforces 511 C)

大致意思是有m个学生要在最短时间内要搬完所有堵在路上的箱子,每一秒学生可以在两件事中任选一件:

1、搬他所在位置的一个箱子

2、向前移动一格

所有人一开始都在0的位置,要在最短时间内搬完箱子,求最短时间。

这题是一个二分加贪心,二分时间,最短设为最远的那个箱子的位置,最长设为n+1+所有箱子的个数/m。

之后就是贪心,当前假设时间为t,看是否可行。我们可以从最后一个有箱子的格子开始,要走到最后一个有箱子的格子需要t1时间,那走到那一格还剩余 t - t1 的时间,可以用那些剩余时间搬当前格子上的箱子,如果时间有剩余,可以搬取前面格子中的箱子,相当于先搬了前面的箱子然后走到后面搬后面的箱子。依次往前搬,详情见代码注释。

#include<iostream>
using namespace std;
const int MAXN=100012;
int n,m;
long long int a[MAXN],b[MAXN];
int doit(long long int mid)//计算mid这个时间是否可行
{
    int i,j;
    long long int x,y,m1;
    i=n; //当前格子,从后往前做
    m1=m;//m1为可用人数
    for (j=1;j<=n;j++) b[j]=a[j];//另建数组方便计算
    while (i>0)
    {
        while (i>0&&b[i]==0) i--; //如果格子为空,往前计算
        if (i==0) return 1;//如果格子都为空,即可行,返回
        x=mid-i; //x为到这个格子还剩余的时间
        if (x<=0) return 0; //如果到这个格子时间已经小于等于0,但这个格子上还有箱子,不可行,返回
        m1-=b[i]/x; 总人//减去用剩余时间去搬这个格子上的箱子,并把时间都用完的人  
        if (m1<0) return 0; //如果人数已经小于0,说明搬不玩,不可行,返回
        if (b[i] % x==0) b[i]=0;//当前这个格子的箱子搬完了
        else if (b[i] % x!=0) //还没搬完
        {
            m1--; //要再用一个人
            if (m1<0) return 0;//如果人数已经小于0,说明搬不玩,不可行,返回
            y=x-(b[i] % x);//此人搬完这个格子还剩余的时间,说明他前面可以再搬点箱子,再往后走
            b[i]=0;//当前箱子搬完
            while (y>0)
            {
                while (i>0&&b[i]==0) i--;
                if (i==0) return 1;
                if (y>=b[i]) { y-=b[i]; b[i]=0; }//如果时间比当前箱子数目多,可以搬完当前的箱子,还可以搬前面的
                else { b[i]-=y; y=0;  } //时间用完了
            }
        }
        if (m1==0) break;//人数用完
    }
    while (i>0&&b[i]==0) i--;
    if (i>0) return 0;//没搬完,不可行
    return 1;//搬完了,可行
}
int main()
{
    int i;
    long long int sum,l,r,mid;
    sum=0;
    cin>>n>>m; a[0]=0;
    for (i=1;i<=n;i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    l=0; r=n+sum;//l和r可以往两边取,反正二分很快0.0
    while (l<r)
    {
        mid=(r+l)/2;
        if (doit(mid)) r=mid; else l=mid+1;
    }
    cout<<r<<endl;
    return 0;
}



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值