USACO 2017 February Contest Silver

来源:互联网 发布:dos攻击域名 编辑:程序博客网 时间:2024/05/24 01:00

在开始总结之前容我兴奋,这次可能是我这小半年里发挥最好的一场了T T

T1.Why Did the Cow Cross the Road

题目描述
FJ的奶牛们在寻找有效地越过马路的方法。因为记得“为何鸡(chicken)要过马路”的笑话,奶牛们发现了鸡一定是过马路方面的行家,于是就起身去找鸡来帮助他们。 结果奶牛们发现,鸡是很忙的动物,并且鸡只会在有限的时间来帮助奶牛。

有C只鸡在农场里(1≤C≤20000),为了方便,将他们标记为1…..C,并且每一只鸡i愿意在精确的Ti时刻帮助奶牛。

而对奶牛们而言,他们从不匆忙,对时刻表有更大的宽松灵活性。

有N只奶牛在农场上(1≤N≤20000),为了方便,将他们标记为1…..N,奶牛j能在时间段Aj~Bj之间穿过马路。

对于奶牛们而言,和鸡配对是最好的越过马路的方式,每一只奶牛j会找到一只理想的鸡i来帮助他越过马路;为了使鸡和奶牛的时间表能吻合,i和j必须满足Aj≤Ti≤Bj.

如果每只奶牛最多能与一只鸡配对并且每只鸡也最多只能和一只奶牛配对,请帮忙计算鸡和奶牛配对的组数的最大值。

输入格式
第一行包含C和N。接下来的C行包含T1…TC,

接下来的N行包含Aj和Bj(Aj< Bj),j=1….N。

A,B,T都是非负整数(不一定互不相同)在0~100000000范围内。

输出格式
输出鸡和奶牛配对的组数的最大值。

说实话,我觉得银组三题里这题是最难的……从全方位解释了什么是贪心只能过样例。
最开始依照线段覆盖的思路,奶牛的右端点升序,鸡也升序,然后第一只鸡配第一头牛,谁配不上谁跳掉。
自以为很对,然而是有反例的:
这里写图片描述

……呃,就是说有两头牛,排完序之后是第一头是5~8,第二头4~9.有两只鸡,在4和5.如果按以上的策略就会把4跳过,把5给第一头,然后就没有然后了,因为到这里我的完美策略就失败了。经过我的深思熟虑,我还是没能想到一种快速的贪心策略,于是走投无路的我选择了暴力。是的,暴力。
仍然把牛右端点升序,把鸡升序,此时我们谁都不能跳,我选择把每头牛枚举一遍,在所有鸡中找到最靠前的那一只。这个道理很显然,如果选靠后的就会浪费资源耽误别人相亲。找到之后把鸡删了,此时我想到动态数组,然而我并不会用,于是我开始沉思20000^2会不会让我爆炸,在我的预算里是会的。于是,我又面临另一个问题:
我是要错误的贪心还是正确的TLE?
我胆子很小,我选择正确的TLE。
于是,下面是我的代码。

#include<bits/stdc++.h>using namespace std;int n,c,ans=0;struct niu{    int t,w;}cow[20020];int cock[20020]={};bool mycmp(niu a,niu b){    return((a.w<b.w)||(a.w==b.w&&a.t<b.t));//其实并不需要所谓第二关键字。至于为什么,实践出真知,我也不知道。}int main(){    cin>>c>>n;    for(int i=1;i<=c;i++) cin>>cock[i];    for(int i=1;i<=n;i++) cin>>cow[i].t>>cow[i].w;    sort(cow+1,cow+n+1,mycmp);    sort(cock+1,cock+c+1);     int i=c;    for(int j=1;j<=n;j++)    {         if(i>0)        for(int k=1;k<=c;k++)             if(cock[k]>=cow[j].t&&cock[k]<=cow[j].w) //如果配对            {                cock[k]=-1;ans++;i--;break;//把这只鸡赋为-1,他就不能祸害别的别的牛了。            }        if(i<=0) break;//如果鸡配完了,就结束吧。    }    cout<<ans;    return 0;}

我坚定不移地相信我会TLE,可是并没有………………………………我至今无法理解,可能是因为那些小小的break吧,我爱您。
不死心,去看官网的题解,发现官网比我更暴力,它的鸡连排序都不带排的,直接上手搜索。然而,然而人家会动态数组啊。

可能还有更简单更快的正解。

T2.Why Did the Cow Cross the Road II

题目描述
传说中伟大的雷克萨的家中有N条街道,分别标记为1……N(1≤N≤100000)。

为了允许他养的小斑虎穿过这些街道,他让雷鸣龙为他设置了N个信号灯,信号灯亮了,小斑虎可以通过,反之,则不行。

不幸的是,萨尔操控雷电毁掉了其中B个灯!!!

可爱的小斑虎回不了家了,怎么办呢???

雷克萨必须修复几个信号灯使有K个连续的灯可以正常使用,来帮助小斑虎顺利回家>-<

输入格式

第一行三个数:N,K,B(1≤B,K≤N)

后面B行为损坏的路灯的编号

输出格式
一个数字:输出修复路灯的最小数量X,使修复后满足有连续的K个正常的信号等

暴力的我起初当然是想暴力地直接枚举,O(N*K),看到数据规模就呵呵了。画了画图,想到一种优化。
这里写图片描述
例如k=3时,先框住前k个数,统计其中坏掉的灯的数目,修完这几个就可以顺利回家了。接下来只需要把这个框右移,即减去上一个数,加上新增的那个数。

#include<bits/stdc++.h>using namespace std;int n,k,b;int a[100005]={}; int minn=1000005;int s=0;int main(){    cin>>n>>k>>b;    for(int i=1;i<=b;i++)    {        int x;        cin>>x;        a[x]=1;    }    for(int i=1;i<=k;i++) s+=a[i];    minn=min(minn,s);    for(int i=2;i<=n-k+1;i++)    {        s=s-a[i-1]+a[i+k-1];//这是唯一要注意的地方,画个图就能明白为什么是这两个数啦。        minn=min(minn,s);    }    cout<<minn;    return 0;}

T3.Why Did the Cow Cross the Road III

题目描述
为什么要为难可爱的奶牛们穿过危险的马路?一个很重要的理由是因为FJ的农场有非常多的路。所以善良的FJ要把农场重新规划成一个NxN的方格网(2≤N≤100)。但是为了维持必要的交通秩序,FJ还是修建了R条路,它们各自插在某两块相邻的田野里。

另外,一道巨大的栅栏围住了整个农场的外围——为了不让奶牛跑出去。那么,现在奶牛们可以自由地从一块田滚到相邻的另一块田(东南西北都属于相邻)。记住,他们仍然无比痛恨过马路!

FJ的农场里有k头奶牛(1≤K≤100,K≤N^2),每一只都在不同的田野里。

现在我们有一个新定义:如果奶牛A去拜访奶牛B时需要过马路,那么他们就是一对”遥远的奶牛“。为了建设和谐奶牛社会,FJ希望你帮助他计算农场中有多少对遥远的奶牛。

输入格式
第一行包括N,K,R。

接下来的R行描述了R条路,其中每一行包括四个1~N之间的整数r1,c1,r2,c2,表示这条路隔开了第r1行c1列的田野和第r2行c2列的田野。

最后的k行描述了k头奶牛的位置,每一行包括两个1~N的整数r3,c3,代表它在第r3行c3列的田野。

输出格式
输出一个数字,表示遥远的奶牛的对数。

样例数据
input
3 3 3
2 2 2 3
3 3 3 2
3 3 2 3
3 3
2 2
2 3

output
2

它的图应该长成这样,方块是田,红色的线是他们的大马路。 没有路相隔的田之间都可以滚来滚去。
这里写图片描述

为什么这道题加了样例输入输出呢,因为我觉得我翻译得超级可爱。而且这个图我画得好严谨…………

回到正题上,这道题翻译的时候想到判连通块,于是乎就没再细想。今天考的时候着手做才发现一个艰难的问题,我根本不知道怎么建这个图。也就无从完成美妙的洪水填充……
后来,聪慧如我,想到了一个很蛋疼的方法,相信AC的dalaos一定会有更简单的,但是不管了。
比如说样例那张图。我把它扩展成这样:
这里写图片描述

棕色的是真正的田野,而他们之间的那些空白格是不存在的道路,奶牛可以放肆地滚过去。打叉的是真的道路。
就在我要下手写的时候,我突然发现一个问题,所以我把图改成了这样:
这里写图片描述
因为那些在田野间对角线上的格子是滚不过去的,奶牛只能上下左右移动,所以那些格子都应该被判定为障碍。
接着就是寻常的染色。具体在当初的找贝西里写过了,就注释得比较简单了。怕MLE,没敢用多维数组判断奶牛是否遥远,我选择浪费一些时间。(实际上并不会)

#include<bits/stdc++.h>using namespace std;int a[300][300]={};//我们的地图。由于每两块田之间都多了个空格,所以我开大了一点。int n,k,r;int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};//方向数组。int ans=0;struct cow{    int x,y;//奶牛的位置。}b[100002];void dfs(int x,int y,int k){    for(int i=0;i<4;i++)    {        int x1=x+dx[i],y1=y+dy[i];        if(a[x1][y1]==0)//如果这个位置还没被染过色而且还可以走。        {            a[x1][y1]=k;dfs(x1,y1,k);        }     }    return;}void input_(){    scanf("%d%d%d",&n,&k,&r);    for(int i=1;i<=r;i++)     {        int r1,c1,r2,c2;        scanf("%d%d%d%d",&r1,&c1,&r2,&c2);        r1=2*r1-1;c1=2*c1-1;r2=2*r2-1;c2=2*c2-1;        a[(r1+r2)/2][(c1+c2)/2]=1;//看上去有点玄学,实际上只是把它处理成转换后的图的坐标,下面的奶牛同理。    }    for(int i=1;i<=k;i++)    {        int r3,c3;        scanf("%d%d",&r3,&c3);        r3=2*r3-1;c3=2*c3-1;        b[i].x=r3;b[i].y=c3;    }    return;}void work_(){    for(int i=0;i<=2*n;i++) a[i][0]=a[0][i]=a[2*n][i]=a[i][2*n]=1;//四周封上。    for(int i=2;i<=2*n-2;i+=2)         for(int j=2;j<=2*n-2;j+=2) a[i][j]=1;//对角线也封上。    int s=1;//在这里,我选择把每个连通块染上不同的颜色。由于1和0都有特殊意义,所以为了避免误判,染色记号从2开始。    for(int i=1;i<=2*n-1;i++)        for(int j=1;j<=2*n-1;j++)        {            if(a[i][j]==0)            {                s++;a[i][j]=s;                dfs(i,j,s);//把s也传过去。            }//新的连通块!        }}void check_(){    for(int i=1;i<=k;i++)        for(int j=i+1;j<=k;j++)            if(a[b[i].x][b[i].y]!=a[b[j].x][b[j].y]) ans++;//刚敲下这段的时候是担心超时的,算了算,最大的数据规模应该会带来(10000*10000/2),放心了一点。事实证明离超时还差很远,果然我辈蒟蒻永远不懂评测机的心啊。}int main(){    input_();    work_();    check_();     cout<<ans;    return 0;}

写完了。其实这几天有点水还有点颓,而且真的坐得浑身发痛。……虽然很多时候蒙混自己,但是清醒的时候不得不承认,我还没有完全从初赛的阴影里出来。和朋友断断续续聊了很多关于未来的事情,简单地归纳起来就是无论现状如何都要走下去,过去都丢掉,要走下去。为了自己也为了身后的人,可能更为了未来有我希望中的朋友。总之,努力吧。哎,努力吧。

阅读全文
1 0
原创粉丝点击