[SinGuLaRiTy] 2017-07-21 综合性测试

【SinGuLaRiTy-1028】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

对于所有题目:Time Limit: 1s | Memory Limit: 256MB

[USACO2016 Jan] 愤怒的奶牛 (Angry Cows)

题目描述

在数轴x上摆放有n(2<=n<=50000)捆干草堆,没有任何两堆在同样的位置,所有的位置均为整数。你可以用弹弓射击射击数轴上的任意地点。如果你用弹弓以R的力度射击x处,那么该处会发生爆炸,爆炸的范围是以R为半径的圆形区域,所以它会使得[x-R,x+R]的所有干草堆同时发生爆炸。这些干草堆的爆炸半径是R-1。它们又会触发连锁反应,第三轮的爆炸的半径为R-2,依次递减。请选择最小的力度射击,使得所有的干草堆全部爆炸。

输入

第一行包含N。接下来N个整数,表示干草堆的位置。所有位置在[0,1000000000]内。

输出

输出最小的力度R,使得所有的干草堆发生爆炸。四舍五入保留一位小数。

样例数据

样例输入样例输出

5
8
10
3
11
1

3.0

 

 

 

 

 

 

<样例解释>

如果以力度3射击坐标5,则坐标3,坐标8处的干草堆会发生爆炸,然后又会引爆坐标1和坐标10的干草堆,最后引爆坐标11处的干草堆。

解析

f[i]表示要炸掉i之前的所有干草堆,在i点最少需要的半径。
g[i]表示要炸掉i之后的所有干草堆,在i点最少需要的半径。
找到满足(j<i&&a[i]-a[j]>f[j]+1)的最后一个j,f[i]=min(a[i]-a[j],f[j+1]+1),g数组同理。
最后枚举起始的射击区间,计算答案。
由于答案的小数部分只可能是0或者0.5,所以将所有数乘以2就可以避免小数问题,最后答案再除以2就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>

#define ll long long
#define N 50005
#define inf 2000000000

using namespace std;

int n,a[N],f[N],g[N];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        a[i]*=2;
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
        f[i]=g[i]=inf;
    int t=1;
    f[1]=0;
    for(int i=2;i<=n;i++)
    {
        while(t+1<i&&a[i]-a[t+1]>f[t+1]+2)
            t++;
        f[i]=min(a[i]-a[t],f[t+1]+2);
    }
    t=n;
    g[n]=0;
    for(int i=n-1;i>=1;i--)
    {
        while(t-1>i&&a[t-1]-a[i]>g[t-1]+2)
            t--;
        g[i]=min(a[t]-a[i],g[t-1]+2);
    }
    int ans=inf;
    for(int i=1,j=n;i<j;)
    {
        ans=min(ans,max((a[j]-a[i])/2,max(f[i],g[j])+2));
        if(f[i+1]<g[j-1])
            i++;
        else
            j--;
    }
    printf("%.1lf",(double)ans/2);
    return 0;
}

[USACO2016 Jan] 无线电通信 (Radio Contact)

题目描述

农夫约翰和奶牛贝西要去寻找丢失的奶牛,为了彼此能联系对方,他们带着无线电通讯设备。不幸的是电池快没有电了。所以它们要尽量节省电能。农夫从位置(fx,fy)出发,一共走N步,贝西从位置(bx,by)出发,一共走M步。农夫的路线是由一个长度为N的字符串限制,字符串只出现’E’或’S’或’W’或’N’中,表示东南西北四个方向。农夫每一单位时间可以选择不动,或者按照限制走出一步。奶牛贝西也是如此。现在无线电设备每一单位时间消耗的电量等于他们的距离的平方。请问,他们走到终点,最少消耗多少电量?

输入

第一行两个整数n,m(1<=n,m<=1000),第二行为fx,fy表示农夫的起始位置,第三行为bx,by,表示贝西的起始位置。
接下来有两个字符串,第一个字符串表示农夫的路线,第二个字符串表示贝西的路线。
他们的坐标总是在(0<=x,y<=1000)。北是y的正方向,东为x的正方向。

输出

输出一个整数,表示最少消耗的电量。

样例数据

样例输入样例输出

2 7
3 0
5 0
NN
NWWWWWN

28

 

 

 

 

 

 

解析

其实,这是一道水题。
设f[i][j]表示当前第一个人走了i步第二个人走了j步,那么就有f[i+1][j]=min(f[i+1][j],f[i][j]+dis())。然后f[i][j+1]和f[i+1][j+1]用同样的方法推出来。

Code

#include<cstdio>
#include<iostream>
#include<cstring>

#define N 1024

typedef long long ll;

using namespace std;

int n,m,a[N][2],b[N][2];
ll f[N][N];
char s[N];

inline int dis(int a,int b,int c,int d)
{
     return (c-a)*(c-a)+(b-d)*(b-d);
}

int main()
{
    scanf("%d%d",&n,&m);
    cin>>a[1][0]>>a[1][1]>>b[1][0]>>b[1][1];
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='N')
            a[i+1][0]=a[i][0],a[i+1][1]=a[i][1]+1;
        else if(s[i]=='S')
            a[i+1][0]=a[i][0],a[i+1][1]=a[i][1]-1;
        else if(s[i]=='W')
            a[i+1][0]=a[i][0]-1,a[i+1][1]=a[i][1];
        else if(s[i]=='E')
            a[i+1][0]=a[i][0]+1,a[i+1][1]=a[i][1];
    }
    scanf("%s",s+1);
    for(int i=1;i<=m;i++)
    {
        if(s[i]=='N')
            b[i+1][0]=b[i][0],b[i+1][1]=b[i][1]+1;
        else if(s[i]=='S')
            b[i+1][0]=b[i][0],b[i+1][1]=b[i][1]-1;
        else if(s[i]=='W')
            b[i+1][0]=b[i][0]-1,b[i+1][1]=b[i][1];
        else if(s[i]=='E')
            b[i+1][0]=b[i][0]+1,b[i+1][1]=b[i][1];
    }
    for(int i=1;i<=n+1;i++)
        for(int j=1;j<=m+1;j++)
            f[i][j]=2147483647;
    f[1][1]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            f[i+1][j]=min(f[i+1][j],f[i][j]+dis(a[i+1][0],a[i+1][1],b[j][0],b[j][1]));
            f[i][j+1]=min(f[i][j+1],f[i][j]+dis(a[i][0],a[i][1],b[j+1][0],b[j+1][1]));
            f[i+1][j+1]=min(f[i+1][j+1],f[i][j]+dis(a[i+1][0],a[i+1][1],b[j+1][0],b[j+1][1]));
        }
    printf("%I64d",f[n+1][m+1]);
    return 0;
}

[USACO2016 Jan] 熄灯 (Lights Out)

题目描述

谷仓是一个简单的多边形。它的每一条边或平行于x轴,或平行于y轴。从任意一个点沿顺时针方向走,横边和纵边是交替出现的。多边形一共有n个点,编号为1到n。其中1号点为出口。奶牛贝西已经完全记住了谷仓的地图。它从某一个点出发,只能沿着边界走,在开灯的情况,它能很快知道怎么走才能更快到达出口。可是有一天,灯熄灭了。它一下慌了神,然后它忘了自己在哪个点了。但是幸运的是它仍然记得整个谷仓的地图,而且它能够凭触觉知道当前点的内角有多大,它也能感觉到当前点是不是出口,而且经过一条边后也能精确地计算出该边的长度。现在,为了寻找出口,它决定采取这样的策略:沿着顺时针方向继续走下去,直到它能够判断出自己的位置,然后再选择距离最短的方向(顺时针或逆时针)走到出口。求最坏的情况下,贝西走到出口要比灯没坏的情况下多花多少时间。

输入

第一行包含N(4<=N<=200),接下来N行每行包含两个整数,表示多边形的n个顶点(xi,yi),按顺时针的方向给出。这些点的范围均在[-100000,100000]

输出

输出贝西按照上述策略在最坏情况下比正常情况下要多花的时间。

样例数据

样例输入样例输出

4
0 0
0 10
1 10
1 0

2

 

 

 

 

 

 

<样例解释>

贝西如果在出口处(Point 1),肯定可以感知到,这绝对不会是最坏情况。现在考虑它在其他3个点时的情形:它当前可以感知到内角的大小。但因为内角都是90度,所以他无法确定自己位置。于是它按照既定的策略,顺时针走:
1.如果它在开始在点2,它需要走到点3,此时,它知道自己在哪里了。于是它找最短的路径,不管哪边,都是11.所以,它一共需要走12个单位。如果是开灯的情况下,只需要走10个单位。所以,它要多走两个单位。
2.如果它在点3,它要走11个单位。开灯的情况下也要走11个单位。
3.如果它在点4,它要走1个单位。开灯的情况下也要走1个单位。
于是最坏的情况下,它要多走两个单位。

解析

这道题,其实最重要的就是贝西判定当前的位置的方法:通过走过一段独一无二的路线。那么,我们怎么用代码进行实现呢?在这里,我用的是vector套pair,这样写也显得有些混乱;有一种用hash来判断的方法,相比之下还是好理解一点。(hash是一个很重要的东西,以后的字符串处理常常会用到) 大体思路就是通过用角度的大小(90度和270度)和边的长度来表达区分不同的路线,如果这个特征值一样,我们就认为贝西无法区分,也就找不到自己的位置;如果特征值不同,贝西就可以辨别出方向了。需要注意的是:数据的处理以及表达方式一定要确保能描述每一条路线的特征,而不会出现不同的路线有相同特征值的情况。

Code

#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
#include<cstdio>

#define MAXN 210

using namespace std;

int opt[MAXN];

int main()
{
    int N;
    scanf("%d",&N);
    vector<pair<long long,long long> > A(N);
    for(int i=0;i<N;i++)
    {
        cin>>A[i].first>>A[i].second;
    }
    vector<int> S(1,0);
    for(int i=0;i<N;i++)
    {
        int j=(i+1)%N;
        int k=(i+2)%N;
        S.push_back(abs(A[i].first-A[j].first)+abs(A[i].second-A[j].second));
        if((A[i].first-A[j].first)*(A[k].second-A[j].second)-(A[k].first-A[j].first)*(A[i].second-A[j].second)>0)
            S.push_back(-1);
        else
            S.push_back(-2);
    }
    S.back()=0;
    for(int i=0;i<N;i++)
    {
        opt[i+1]=opt[i]+S[i*2+1];
    }
    opt[N]=0;
    for(int i=N-1;i>=0;i--)
    {
        opt[i]=min(opt[i],opt[i+1]+S[i*2+1]);
    }
    multiset<vector<int> > st;
    for(unsigned int i=0;i<S.size();i+=2)
    {
        for(unsigned int ln=1;i+ln<=S.size();ln+=2)
        {
            st.insert(vector<int>(S.begin()+i,S.begin()+i+ln));
        }
    }
    int result=0;
    for(unsigned int i=2;i+2<S.size();i+=2)
    {
        int ln;
        int cost=0;
        for(ln=1;;ln+=2)
        {
            if(st.count(vector<int>(S.begin()+i,S.begin()+i+ln))==1)
                break;
            cost+=S[i+ln];
        }
        result=max(result,cost+opt[(i+ln)/2]-opt[i/2]);
    }
    cout<<result;
    return 0;
}

[Vijos 1157] 分梨子

题目描述

Finley家的院子里有棵梨树,最近收获了许多梨子。于是,Finley决定挑出一些梨子,分给幼稚园的宝宝们。可是梨子大小味道都不太一样,一定要尽量挑选那些差不多的梨子分给孩子们,那些分到小梨子的宝宝才不会哭闹。每个梨子都具有两个属性值,Ai和Bi,本别表示梨子的大小和甜度情况。假设在选出的梨子中,两个属性的最小值分别是A0和B0。只要对于所有被选出的梨子i,都满足C1*(Ai-A0)+C2*(Bi-B0)≤C3(其中,C1、C2和C3都是已知的常数),就可以认为这些梨子是相差不多的,可以用来分给小朋友们。那么,作为幼稚园园长的你,能算出最多可以挑选出多少个梨子吗?

输入

第一行一个整数N(1≤N≤2000),表示梨子的总个数。 第二行三个正整数,依次为C1,C2和C3(C1,C2≤2000,C3≤10^9)。 接下来的N行,每行两个整数。第i行的两个整数依次为Ai和Bi。

输出

只有一个整数,表示最多可以选出的梨子个数。

样例数据

样例输入样例输出

3
2 3 6
3 2
1 1
2 1

2

 

 

 

 

 

 

解析

神奇的题~

对于这道题,我们要采用"数形结合"的思想:每一个梨子不是有两个特征值Ai和Bi吗,我们要把它们看做一组坐标(Ai,Bi),这样一来,每个梨子就变为了坐标轴上的一个点。我们来继续处理给出的题目信息:在选出的梨子里,有两个最小值,也就是说,对于所有我们选中的梨子,都满足:Ai>A0,Bi>B0,也就是说,所有的我们选中的梨子所代表的的点,都应该位于直线x=A0的右边,直线y=B0的上方。<是不是很"神奇"?>我们继续,题目又说了:每一个梨子(点)都满足C1*(Ai-A0)+C2*(Bi-B0)≤C3,我们用更"数学"的语言翻译一下,就变成:“每一个点(X,Y)都满足M*(X-a)+N(Y-b)≤C (M,N,C均为常数,我们在这里也暂且把a和b当做常数)”,嘿,这不是高中数学教材里的线性规划不等式吗?下面我们来画一画图(图中的点代表梨子):

如图,三条直线围成的这个直角三角形内的点就是我们能够选择的梨子了。欢呼了?雀跃啦?不行,现在直接枚举A0、B0,就已经是O(n^2)了,然后再在三角形里面扫描满足条件的梨子,O(n^3)直接超时。于是乎,我们要想一个法子,让扫描梨子这一项的工程量减小一点。我们先将原来的不等式变为:C1Ai+C2Bi-C3<=C1A0+C2B0 。然后我们可以这么搞:先在外层来一个for循环确定A0,接下来我们再由低到高枚举B0,这时看一看我们刚刚得出的不等式,发现等式右边的值变大了,也就是说会有新的梨子满足这个不等式。——哎,有法子了!我们在枚举A0、B0之前预处理一下:1>记录每一个y轴(y=1,y=2,y=3......)上的梨子(点)的数量,2>将梨子(点)按C1Ai+C2Bi-C3排序。我们在枚举B0的过程中,先删去由于B0增大而从三角形下方剔除出去的梨子(由于有了<1>预处理,这个过程就是O(1)的了),再加上新的满足不等式的值(<2>预处理也大大见减少了时间),不断更新,然后取最大值,最后就能得到答案了。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std; const int MAXN=2005;
struct node { int a,b,s; }p[MAXN];
int c1,c2,c3; int ans; int n; inline bool cmp(const node&a,const node&b) { return a.s<b.s; } void init() { scanf("%d",&n); scanf("%d%d%d",&c1,&c2,&c3); for(int i=1;i<=n;i++) { scanf("%d%d",&p[i].a,&p[i].b); p[i].s=p[i].a*c1+p[i].b*c2; } } int main() { init(); sort(p+1,p+n+1,cmp); for(int i=1;i<=n;i++) { int a0=p[i].a; int b0=p[i].b; int cur=i; int tot=0; while(p[cur++].s<=c1*a0+c2*b0+c3&&cur<=n) tot++; ans=max(ans,tot); } cout<<ans<<endl; return 0; }

 

Time: 2017-07-21

转载于:https://www.cnblogs.com/SinGuLaRiTy2001/p/7218156.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值