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)