【原创】2018.07.07 考试 //生气的奶牛 无线电通信 奶牛修 堡垒

版权声明:未经过作者允许,QωQ是可以转载的,只不过要赞一下本文章并发评论告诉我,然后转载附上原网址就好了!=QωQ= https://blog.csdn.net/c20182030/article/details/80961174

20180707考试

生气的奶牛

时间限制: 1 Sec 内存限制: 128 MB

题目描述

在数轴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处的干草堆。

分析

这个题:如果力度足够大,肯定可以炸完。因此,满足二分答案题目性质。
二分这个“力度”。
怎么检验?
(检验方法千奇百怪的)我说说我的想法:
我们不可能再二分爆炸位置了(记住这句话),毕竟10^9。

过来想,
如果这个力度够大,那么从1到n所有的点都会爆炸。
emm……..
如果这个力度够大,那么从1到n所有的点都会爆炸。
emm…….
那我能不能从1开始炸呢?
然后就想到了:
分别从1和n往中间炸,尽可能地炸,直到两者汇合<=>炸完全程<=>两端区间重合。

我说也说不清楚,看看我的代码:
(打问号的表示这是一通乱调试调出来的,↑表示我这个减号原来是加号,我这个AC代码原来是50分)

代码

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

void Read(int &p)
{
    p=0;
    int f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
        p=p*10+c-'0',c=getchar();
    p*=f;
}


const int MAXN=50000+25;
int n,a;
double ans,p[MAXN],r[MAXN];

//分别从1向右和n向左炸,炸到力度==mid为止,看两者所炸的区间有无交集
bool check(double mid)
{
    r[1]=0;
    double L,R;
    for(int i=1;i<=n;)
    {
        if(i==n) return 1;
        int j=i+1;
        while(p[i]+r[i]+1>=p[j]&&j<=n) j++;
        /***********?**********/
        j-=(j!=i+1);
        if(p[i]+mid-1<p[j]) {R=p[i]+mid;break;}
        r[j]=max(r[i]+1,p[j]-p[i]);
        if(mid-2<r[j]&&r[j]<=mid-1) {R=p[j]+mid; break;} 
        /******?*********?*/
        i=j;
    }
    r[n]=0;
    for(int i=n;i/*>=1*/;)
    {
        if(i==1) return 1;
        int j=i-1;
        while(p[i]-r[i]-1<=p[j]&&j/*>=1*/) j--;
        /**********?*********?????*/
        j+=(j!=i-1);
        /*******↑*****/
        if(p[i]-mid+1>p[j]) {L=p[i]-mid;break;}
        r[j]=max(r[i]+1,p[i]-p[j]);
        if(mid-2<r[j]&&r[j]<=mid-1) {L=p[j]-mid; break;}    
        /******?*********?*/
        i=j;
    }
    return L<=R;
}

int main()
{
    freopen("angry.in","r",stdin);
    freopen("angry.out","w",stdout);
    Read(n);
    for(int i=1;i<=n;i++) Read(a),p[i]=a*1.0;
    sort(p+1,p+1+n);
    double l=0,r=p[n]-p[1],mid;

    while(r-l>0.001)
    {
        mid=(l+r)/2;
        if(check(mid)) ans=r=mid;
        else l=mid;
    }
    printf("%.1lf\n",ans);
}

真香!
某AK大佬表示:
谁说10^9全程都要二分啦?

#include<cstdio>
#include<algorithm>
using namespace std;
int n,a[50005];
bool check(double k)
{
    double l=0,r=a[n],mid,x,y;
    int pos,i,j;
    bool f1,f2;
    while(r-l>0.001)
    {
        mid=(l+r)/2;f1=f2=0;
        pos=lower_bound(a+1,a+1+n,mid)-a;
        i=pos-1,j=pos;
        x=mid,y=k;
        while(i>0)
        {
            if(x-a[i]>y) {f1=1;break;}
            while(i>0&&x-a[i]<=y) i--;
            x=a[i+1],y--;
        }
        x=mid,y=k;
        while(j<=n)
        {
            if(a[j]-x>y) {f2=1;break;}
            while(j<=n&&a[j]-x<=y) j++;
            x=a[j-1],y--;
        }
        if(f1&&f2) return 0;
        if(f1) r=mid;
        else if(f2) l=mid;
            else return 1;
    }
    return 0;
}
int main()
{
    freopen("angry.in","r",stdin);
    freopen("angry.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    double l=0,r=a[n],mid;
    while(r-l>0.001)
    {
        mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%.1lf",l);
}

不说了,心痛,自己看代码。

无线电通信

时间限制: 1 Sec 内存限制: 128 MB

题目描述

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

输入

第一行两个整数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

提示

分析

一眼就看出来是走或不走的2^n的枚举决策的DP。
方程式也一眼列出来:

dp[i][j]表示农夫走i步,奶牛走j步的最小消耗
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+dis[i][j]
dis随便预处理一下就行了

那我为什么只有10分?
蛤?
不是曼哈顿距离?

代码

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

void Read(int &p)
{
    p=0;
    int f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
        p=p*10+c-'0',c=getchar();
    p*=f;
}

const int MAXN=1000+25;
int n,m,k,dp[MAXN][MAXN];
int x1[MAXN],y1[MAXN],x2[MAXN],y2[MAXN];
int h1(char c){return (c=='W'||c=='E')?(c=='W'?-1:1):0;}
int h2(char c){return (c=='N'||c=='S')?(c=='S'?-1:1):0;}
//int dis(int i,int j){return (max(x1[i]-x2[j])-min(x1[i],x2[j])+max(y1[i],y2[j])-min(y1[i],y2[j]))*(max(x1[i]-x2[j])-min(x1[i],x2[j])+max(y1[i],y2[j])-min(y1[i],y2[j]))}
int dis(int i,int j){return (x1[i]-x2[j])*(x1[i]-x2[j])+(y1[i]-y2[j])*(y1[i]-y2[j]);}

int main()
{
    Read(n); Read(m);
    Read(x1[0]); Read(y1[0]); Read(x2[0]); Read(y2[0]);

    char c[MAXN];
    scanf("%s",c+1);
    k=strlen(c+1);
    for(int i=1;i<=k;i++)
        x1[i]=x1[i-1]+h1(c[i]),y1[i]=y1[i-1]+h2(c[i]);

    scanf("%s",c+1);
    k=strlen(c+1);
    for(int i=1;i<=k;i++)
        x2[i]=x2[i-1]+h1(c[i]),y2[i]=y2[i-1]+h2(c[i]);

    memset(dp,120,sizeof dp);
    dp[0][0]=0;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
            if(i) dp[i][j]=min(dp[i][j],dp[i-1][j]);
            if(j) dp[i][j]=min(dp[i][j],dp[i][j-1]);
            if(i&&j) dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
            if(i||j)dp[i][j]+=dis(i,j);
        }
    cout<<dp[n][m]<<endl;
}

堡垒

时间限制: 1 Sec 内存限制: 128 MB

题目描述

贝西要建立一个堡垒。堡垒要想建得稳固,地基要打得扎实。它找了一块地,准备在上面打一个矩形的地基。它找了一片空地,但是空地上有一些沼泽。地基显然不能建在沼泽上。这块地是一个N米*M米的矩形区域。请问贝西能够盖多大面积的堡垒,堡垒必须建在矩形的地基上。(1<=N,M<=200)

输入

第一行有两个整数N,M。

接下来N行,每行包含M个字符,表示这块空地。字符’.’表示正常草地,字符’X’表示沼泽。

输出

一个整数,表示最大的面积。

样例输入

5 6
……
..X..X
X..X..
……
..X…

样例输出

16

分析

这个题比较有迷惑性,但分析一波样例还是能得出,这个地基只需要四条边上没有X就行了,中间有没有X不管(这都什么豆腐渣工程啊喂)

我考试的时候码的:
枚举长(N~1)枚举宽(M~1)枚举起点(i:1~N,j:1~M),暴力检查四条边上有没有X。
时间复杂度:O(N*M*N*M*(N+M))也就是O(n^5)
当然肯定不行,于是乎我掐秒输出,得了77分(满分91)
蛤?

错误代码

#include<ctime>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

void Read(int &p)
{
    p=0;
    int f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
        p=p*10+c-'0',c=getchar();
    p*=f;
}


const int MAXN=200+5;
int N,M,ans;
bool mp[MAXN][MAXN];
char c[MAXN];

bool check(int h,int s,int x,int y)
{
    for(int i=x;i<=x+h-1;i++)
        if(mp[i][y]||mp[i][y+s-1]) return 0;
    for(int j=y;j<=y+s-1;j++)
        if(mp[x][j]||mp[x+h-1][j]) return 0;
    return 1;
}

int main()
{
    freopen("fortmoo.in","r",stdin);
    freopen("fortmoo.out","w",stdout);
    double t=clock();
    Read(N); Read(M);
    for(int i=1;i<=N;i++)
    {
        scanf("%s",c+1);
        int k=strlen(c+1);
        for(int j=1;j<=k;j++)
            mp[i][j]=(c[j]=='X');
    }

    for(int i=N;i>=1;i--)
    {
        if(clock()-t>=955) break;

        for(int j=M;j>=1;j--)
        {
            if(clock()-t>=955) break;
            bool ok=0;

            for(int ii=1;ii<=N-i+1;ii++)
            {
                if(clock()-t>=955||ok) break;
                for(int jj=1;jj<=M-j+1;jj++)
                {
                    ok=check(i,j,ii,jj);
                    if(clock()-t>=955||ok) break;
                }
            }

            if(ok) ans=max(ans,i*j);
        }
    }
    printf("%d\n",ans);
}

只是为了说明暴力有时候真的出奇迹而已。

正确的操作是:
对于每一列,枚举一段区间,如果这一段区间上没有X,并与上一段长度高度相同且没有X且列数不同的区间连成一个矩形,如果上下两条边也没有X的话,则产生一个解。

说的好绕啊。
这里写图片描述

代码贴上来,自己看吧,,我也是看标程看很久才看出来的。

代码

#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

void Read(int &p)
{
    p=0;
    char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') 
        p=p*10+c-'0',c=getchar();
}
const int MAXN=200+5;
int n,m,ans,sum[MAXN],las[MAXN][MAXN];
char c[MAXN];

int main()
{
    Read(n); Read(m);
    memset(las,-1,sizeof las);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",c+1);
        for(int j=1;j<=m;j++)
        {
            sum[j]=sum[j-1];
            if(c[j]=='X') sum[j]++;
        }
        for(int j=1;j<=m;j++)
            for(int k=j;k<=m;k++)
            {
                if(c[j]=='X'||c[k]=='X') las[j][k]=-1;
                else if(las[j][k]==-1&&sum[k]-sum[j-1]==0) las[j][k]=i;
                if(las[j][k]!=-1&&sum[k]-sum[j-1]==0)
                    ans=max(ans,(i-las[j][k]+1)*(k-j+1));
            }
    }
    printf("%d\n",ans)

猜你喜欢

转载自blog.csdn.net/c20182030/article/details/80961174