2017ACM/CCPC中国大学生程序设计竞赛-秦皇岛(A题)

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3981
这个重现赛我真的是坚持到最后20分钟还在写题,虽然最后A题也没过吧,但是总归是写到最后了,希望日后如果现场做比赛也有这淡定!

A题是看着长一些,但是我读了几遍之后就懂了题意,然后又想了一下,感觉思路还挺对的,也不像是啥算法题,此时距离比赛结束还有近两个小时(总不能一直挂机)…就敲了一下,结果超时了,不是很懂为什么超时…比赛结束之后看了一下题解,发现…好像不是像我这样想的(哀嚎啊),但是我觉得我还是可以想想搞定的!

#include<iostream>
using namespace std;
#include<set>
#include<algorithm>
#include<string.h>
typedef struct Pre
{
    long long a,b;
};

Pre pree[100005];

long long n,m,p;
set<long long>ACtime[100005];
long long pos[100005];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        long long num[100005];
        memset(num,0,sizeof(num));
        scanf("%lld%lld%lld",&n,&m,&p);
        int i;
        for(i=1;i<=n;i++)
            scanf("%lld",&pos[i]);
        for(i=1;i<=n;i++)
            ACtime[i].clear();
        for(i=0;i<p;i++)        
        {
            long long a,b;
            scanf("%lld%lld",&a,&b);
            int pnum=ACtime[a].size();
            ACtime[a].insert(b);
            if(ACtime[a].size()>pnum)
            {
                num[a]+=b;
            }
            pree[i].a=a;
            pree[i].b=b%m;
        }
        for(i=1;i<=n;i++)
            num[i]=num[i]%m;

        /*for(i=1;i<=n;i++)
        {
            long long sum=0;
            for(set<long long>::iterator it=ACtime[i].begin();it!=ACtime[i].end();it++)
                sum+=*it;
            sum=sum%m;
            num[i]=sum;
        }*/
        int difficultypos;
        int mmax=-1;
        for(i=1;i<=n;i++)
        {
            if(mmax<num[i])
            {
                mmax=num[i];
                difficultypos=i;
            }
        }

        int dmin=*ACtime[difficultypos].begin();
        dmin=dmin%m;
        int start=(pos[difficultypos]-dmin+1+m)%m;//初始位置
        //cout<<"初始位置:"<<start<<endl; 

        long long arrive[100005];
        for(i=1;i<=n;i++)
        {
            arrive[i]=(pos[i]-start+1+m)%m;
            //cout<<arrive[i]<<" ";
        }
        //cout<<endl;

        long long ans=0;
        for(i=0;i<p;i++)
        {
            //ans+=abs(arrive[pree[i].a]-pree[i].b);
            if(arrive[pree[i].a]>=pree[i].b)
            ans+=arrive[pree[i].a]-pree[i].b;
            else
            ans=ans+arrive[pree[i].a]-pree[i].b+m;
        }
        printf("%lld\n",ans);

    }
    return 0;
}

☝这是我超时的代码

题意:n,m,p三个数字(10^5,10^9,10^5)分别代表n个参赛队伍,m个座位,p条预言,一个机器人从m个位置任意出发,然后每秒钟顺时针移动一个格(到m之后下一个就会去1),然后走到哪里就会把气球发给已经AC的队伍,假设a队伍b时刻解决了一题,结果c时刻拿到气球,那么他们的不开心值就是c-b,要求总的不开心值最小,然后确定机器人的初始位置,进而确定最小的不开心值。

我的思路:位置有10^9个,应该要避开这个,但是参赛队伍的数目和预言的数目都是10^5,都是可以遍历的,经过我对样例的分析(天真,围笑),机器人初始的位置就在那个”AC最多”的队伍处(后来发现都是臆想!)就是把每一个队伍解决的题目的不同的时间(因为相同的就可以一起解决)相加,然后对m取模,排序之后最大的那个,保证它生气值最小,从而确定机器人的初始位置。

看了好多题解,加自己又想了一下,发现我的想法有很多错误之处。(以下纯属瞎扯,估计过段时间自己也看不懂了)
①并不能保证我找到起点就是最优的起点,因为哪怕它是最多的,取它做初始也不能保证它最终用时最少。
②相同的并成一样的解决这个想法就很有问题,因为虽然可以一起解决,但是不开心值也是按照倍数增加的。

下面说一下新的想法:
解决这个题最重要的是找到初始位置,既然已经不能O(1)确定初始位置(其实想想也是,如果能够一下子就找到初始位置,设置这么大的数据的意义在哪里呢,所以这就启示我们,该遍历的时候要遍历,不要随意地下结论,就好像是不知道全部不要瞎逼逼一样,个人感觉ACM大部分题目还是要遍历的,多了解才有发言权),那么就要遍历,位置有10^9个,所以不能遍历,那么就是可以使某些题目的不开心值为0的位置才是我们需要遍历的。

解释一下:
可以把机器人走的路线想成一条直线而不是一个环,那么当他处在一个不存在队伍AC题目的时间时,所要做的就是尽快到达下一个地方,在他后方的就是在他后方,在他前方的可以想象成在他后方,自然是到达那个离他最近的地方为0的地方(不然在路上瞎耽误什么工夫),emmmm还是说不清楚…

那么问题又来了,遍历这些位置就需要10^5,确定这些位置的不开心值只能是O(1)的复杂度,那怎么确定呢?
这里写图片描述
可以发现,机器人的初始位置每向后移动一下,原来为0的变为m-1,原来不为零的减一,这个还是把机器人走的路想成一条线,就会好理解一些。那么我们只需要求出当起始位置为1的时候的总的不开心值,然后以一个O(1)的复杂度的表达式就可以确定每一个初始位置的不开心值。有一点像逆序数那个的,都是先确定一个最初的状态,然后后面的状态都是这个状态的线性的改变。

所以这个题目的难点就在于:
①理解怎么确定起始位置
②确定好起始位置之后怎么求不开心值

#include<iostream>
using namespace std;
#include<algorithm>

long long pos[100005];
long long truestart[100005];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        long long n,m,p;
        long long sum=0;
        scanf("%lld%lld%lld",&n,&m,&p);
        int i;
        for(i=1;i<=n;i++)
            scanf("%lld",&pos[i]);
        for(i=1;i<=p;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            //truestart[i]=(pos[a]+1-b%m+m)%m;
            int temp=(pos[a]+1-b%m+m)%m;
            if(!temp)
            truestart[i]=m;
            else
            truestart[i]=temp;
            cout<<truestart[i]<<endl;
            sum+=(truestart[i]-1)%m;
        }
        //cout<<sum<<endl;
        sort(truestart+1,truestart+p+1);
        long long ans=9999999999;
        for(i=1;i<=p;i++)
        {
            ans=min(ans,sum+(i)*m-p*truestart[i]);
        }
        printf("%lld\n",ans); 
    }
    return 0;
}

乱七八糟的一个代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM-ICPC(国际大学生程序设计竞赛)是一项全球性的大学生程序设计比赛,每年吸引来自世界各地的顶尖大学代表队参与。ACM-ICPC竞赛的核心内容是团队编程和问解决能力。 首先,ACM-ICPC竞赛对参赛选手的编程能力要求很高。参赛队伍需要在规定的时间内解决一系列的算法问,这些问常常包含复杂的数据结构和算法,要求选手在有限的时间内设计和实现高效的程序。 其次,ACM-ICPC竞赛强调团队协作。每个队伍由三名选手组成,他们需要分工合作,保持良好的沟通与协调,共同解决问。团队成员需要相互理解、相互信任,快速地协商和决策,同时要保持高效的任务分配和时间管理。 此外,ACM-ICPC竞赛也需要选手具备良好的问解决能力。这些问往往是实际应用或理论推导相关的,选手需要从数学、计算机科学和算法等多个角度出发,找到最佳解决方案。在面对问时,选手需要对问进行分析、抽象和建模,运用各种算法和数据结构进行解决。 对于参赛选手来说,ACM-ICPC提供了一个学习与交流的平台。在比赛中,选手可以接触到不同国家和地区的优秀程序设计人才,学习他们的思维方式和编程技巧。同时,ACM-ICPC还举办了一系列的培训和研讨会,让选手有机会深入了解计算机科学和算法领域最新的研究成果。 总之,ACM-ICPC国际大学生程序设计竞赛是一个挑战性与学习性兼具的比赛。它要求选手具备扎实的编程技能、团队合作能力和问解决能力。参与此竞赛不仅可以锻炼自己的编程能力,还能与全球的顶尖程序设计人才进行交流,拓宽自己的视野和思维方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值