cf div2 2018.11.28

我又开始我的cf之旅,dei,这次是和夜猫汁小分队在此处 @小夜猫籽们,这次要坚持下去了呢

 

 鉴于这次ljp小朋友让我和lk小朋友试讲一下题,所以我就写一下这次cf的(签到题 )题解

A. Vasya and Book

【传送门】http://codeforces.com/contest/1082/problem/A

大体意思就是给一个n代表一共有从1到n个数代表一本书有n页,给定x,y和一个距离d,每次只能前进或者后退d页,

例如,如果一本书有10页,d=3,那么Vasya按下其中一个按钮,就可以从第1页滚动到第1页或第4页;从第2页到第1页或第5页;从第6页到第3页或第9页;从8号到5号或10号。

求从x页到y页的最小的方法,注意x,y范围,虽然没有明确指出,但是理解数据范围可以看出,存在x>y的情况

这个题给的栗子特别重要,因为书的页数不可能翻出去,所以再怎么往前翻极端情况就是第1页,再怎么往后翻极端情况就是最后一页。这是特别重要的一点,可以利用翻到第一页和最后一页来中转到第y页

这个题的难点就在于思维。可以分情况讨论,

我们可以讨论到达的,剩下的就是不能到达了

首先x和y如果是同一页,那不用说,肯定可以,步数为0;

1.如果x和y之间所差的页数,正好是d的倍数,即(y-x)%d==0那肯定也以到达,步数即为|(y-x)/ d |(注意绝对值)其实这种情况可以包括上面的情况,因为0也可以做除数鸭

下面再在(y-x)不是d的倍数讨论

2.接下来再讨论可以经过第1页最后一页中转而到达y的情况。

(1)经过第1页和最后一页中转都到达y,即(y-1)%d==0&&(n-y)%d==0就要从两种里面取步数少的那种,注意要分别加上对应的从x到1和n的步数然后取最小。

再考虑一个极端情况,若y==1||y==n,那也一定会到达,因为不是倍数那肯定要多走一次才能到达y了,步数为|(y-x)/d |+1

(2).1只能经过第1页中转到y,其中包括极端情况y==1,

(2).2只能经过最后一页到y,其中包括极端情况y==n.

好了,看AC代码


#include<bits/stdc++.h>
using namespace std;
int t;
int n,x,y,d;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        cin>>n>>x>>y>>d;
        if(abs((y-x))%d==0)//距离是d的倍数,直接到达
            cout<<abs((y-x)/d)<<endl;
        else//距离不是d的倍数,要多走一次
        {
            if((y-1)%d==0&&(n-y)%d==0)//两次中转都可以到达
                cout<<min((y-1)/d+(x-1)/d+1,(n-y)/d+(n-x)/d+1)<<endl;//取最小值,记得加分别到1和n的距离
            else if((y-1)%d==0)//经1中转
                cout<<(y-1)/d+(x-1)/d+1<<endl;
            else if((n-y)%d==0)//经n中转
                cout<<(n-y)/d+(n-x)/d+1<<endl;
            else//不能到达情况
                cout<<"-1"<<endl;
        }
    }
}

 

B.Vova and Trophies

【传送门】http://codeforces.com/contest/1082/problem/B

经过一次交换或者不交换,问最多有多少个G连续

这个题看起来容易理解,但是实现起来不太好实现(因为我太弱惹ε=ε=ε=(~ ̄▽ ̄)~

可以这样想,先找出最长的段,再找出交换后最长的段 从中选择最长的那一个。

需要注意的是交换时增加的G数,

首先如果全是G,就输出G的个数,全是S输出0

然后两个G段之间间隔一个S时:

例如(1)GGGSGGG,当把所有的G分在两个段,之间间隔一个S并且不再有剩余的G时,合成的G段G数是两个段相加。再例如(2)GGGSGGGSG,当把所有的G分在两个段,之间间隔一个S并且有剩余的G时,合成的G段G数是两个段相加+1

两个段之间间隔两个以上S时,

就只需要把最长的段边界上的一个S换成一个G,G个数+1;上面提到的找出最长的段来比较在这就包括了,所以不必再单独拿出来

因为我写的太挫惹,就改一下ljp的代码来参考

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
struct Node
{
    int l,r;
    Node (int _a=0,int _b=0):l(_a),r(_b){};
}N[maxn];
int n;
string s;
int index;//用来标记G段的下标
int totG; //记录总的G个数
int cur=1;//记录G的段数
int main()
{
    cin>>n>>s;
    while(index<n)
    {
        while(index!=n&&s[index]=='S')
            index++;
        int l=index;//S的结束 下一个字母就是G的开始,左边界,用来记录G段
        while(index!=n&&s[index]=='G')
            index++;
        int r=index;//G的结束,记录G段的右边界
        if(l!=r)//左右边界不是同一个数,也就是连续的G>1个
        {
            N[cur]=Node(l,r);//压入N记录一下
            totG+=(N[cur].r-N[cur].l);//右边界-左边界=个数
            cur++;//段数++;
        }
    }
    int x=N[1].r-N[1].l;//第一个段的长度
    if(cur==2)//说明只有一个G段,(压入后cur++了)
    {
        cout<<x<<endl;//只有一个段肯定是这个
        return 0;
    }
    int sum;
    for(int i=2;i < cur;++i)//大于两个G段
    {
        int len1=N[i-1].r-N[i-1].l;//前一个段长
        int len2=N[i].r-N[i].l;//后一个段长
        if(N[i].l-N[i-1].r == 1) //如果两个段之间间隔一个S
            sum=len1+len2+(len1+len2 < totG ? 1:0);//len1+len2 < totG说明有多余的G那么+1
        else//两个段之间间隔大于一个S,
            sum=max(len1,len2)+1;
        x=max(x,sum);//取最大值
    }
    cout<<x<<endl;
}

C.Multi-Subject Competition

【传送门】http://codeforces.com/contest/1082/problem/C

一共n门科目,从中参加m门去比赛,每个人擅长其中一门科目si,可以得到分数ri。参加每门比赛的人数必须相同,也可以不参赛,求可以获得的最大分数

因为数据范围太大惹,我开的vector数组,

记录一下每一门课每个人的分数然后从大到小排序,(我是从小到大排序然后又反转的....没写过迭代器比较函数,所以这样比较保险),然后用S记录一下的和,因为有负数,那..还不如不去比赛(太丢人惹(逃ε=ε=ε=┏(゜ロ゜;)┛,然后用一个数组分别记录第i门课有j的人去参赛的分数和,然后从这个数组中取最大的就可以了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
vector<int> v[maxn];
long long idSum[maxn];
int n,m;
int main()
{
    ios_base::sync_with_stdio(0);
    cin>>n>>m;
    int sub,grade;
    for(int i=0;i<n;i++)
    {
        cin>>sub>>grade;
        v[sub].push_back(grade);
    }

    for(int i=1;i<=m;i++)
    {
        sort(v[i].begin(),v[i].end());//从小到大排序
        reverse(v[i].begin(),v[i].end());//反转 从大到小
        long long sum=0;
        for(int j=0;j<v[i].size();j++)
        {
            sum+=v[i][j];//科目分数和
            if(sum>0)
                idSum[j+1]+=sum;//人数为j+1时的各科目分数和
                                //因为v数组是从0开始的,所以j要+1才是对应的科目编号
        }
    }
    
    long long ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,idSum[i]);

    cout<<ans<<endl;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值