T1 生气的奶牛
这道题有二分和贪心两种解法,我考试时先想到的是二分,但是没有想清楚,得了 46 46 46分。
先给输入的 a a a数组由小到大排个序。
考虑二分我们需要的答案,也就是力度,下界即为 0 0 0,上界明显为 ( a [ n ] − a [ 1 ] ) / 2.0 (a[n]-a[1])/2.0 (a[n]−a[1])/2.0。
接下来思考 c h e c k check check怎么写,我们需要确定这个力度能否使所有干草堆爆炸,还需要确定一个因素,就是爆炸的位置。
我们可以通过二分的方法确定,然后暴力向左右扩展,判断是否可行。
具体可见代码,参照了 c j x cjx cjx的做法。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL MAXN=50005;
LL n;
double a[MAXN];
const double eps=1e-7;
bool check(double x){
double l=a[1],r=a[n];
LL cnt=0;
while(r-l>eps&&++cnt<=100){
double mid=(l+r)/2.0;
LL pos=lower_bound(a+1,a+n+1,mid)-a;
LL left=pos-1,right=pos;
double h=mid,p=x;
bool flag1=true,flag2=true;
while(left>=1){
if(h-p>a[left]){
flag1=false;break;
}
while(left>=0&&h-p<=a[left])left--;
p--;h=a[left+1];
}
h=mid;p=x;
while(right<=n){
if(h+p<a[right]){
flag2=false;
break;
}
while(right<=n&&h+p>=a[right])right++;
p--;h=a[right-1];
}
if(!flag1&&!flag2){
return false;
}
if(!flag1)r=mid;
else if(!flag2)l=mid;
else return true;
}
return false;
}
int main(){
scanf("%lld",&n);
for(LL i=1;i<=n;i++){
scanf("%lf",&a[i]);
}
sort(a+1,a+n+1);
double l=0,r=(a[n]-a[1])/2.0;
LL cnt=0;
while(r-l>eps&&++cnt<=100){
double mid=(l+r)/2.0;
if(check(mid)){
r=mid;
}else{
l=mid;
}
}
printf("%.1lf",l);
}
2023年7月13日更新,更好看的代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL MAXN=50005;
const double eps=1e-7;
LL n;
double a[MAXN];
bool pd1(double pos,double x){
LL left=lower_bound(a+1,a+n+1,pos)-a-1;
double p1=pos,p2=x;
while(left>=1){
if(p1-p2>a[left])return false;
while(left>=1&&p1-p2<=a[left])left--;
p2--;p1=a[left+1];
}
return true;
}
bool pd2(double pos,double x){
LL right=upper_bound(a+1,a+n+1,pos)-a-1;
double p1=pos,p2=x;
while(right<=n){
if(p1+p2<a[right])return false;
while(right<=n&&p1+p2>=a[right])right++;
p2--;p1=a[right-1];
}
return true;
}
bool check(double x){
double l=a[1],r=a[n];
LL cnt=0;
while(r-l>=eps&&++cnt<=100){
double mid=(l+r)/2.0;
bool flag1=pd1(mid,x);
bool flag2=pd2(mid,x);
if(flag1&&flag2){
return true;
}
if(!flag1){
r=mid;
}
if(!flag2){
l=mid;
}
}
return false;
}
int main(){
scanf("%lld",&n);
for(LL i=1;i<=n;i++){
scanf("%lf",&a[i]);
}
sort(a+1,a+n+1);
double l=0,r=(a[n]-a[1])/2.0;
LL cnt=0;
while(r-l>=eps&&++cnt<=100){
double mid=(l+r)/2.0;
if(check(mid)){
r=mid;
}else{
l=mid;
}
}
printf("%.1lf",r);
}
T2 无线电通信
路线已经固定,且必须走完,这就给我们一个启发,可以用 d p dp dp。
设 d p [ i ] [ j ] dp[i][j] dp[i][j]为当前农夫走了 i i i步、奶牛走了 j j j步,最少消耗的电量。
方程即为 d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + d i s ( i , j ) dp[i][j]=min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+dis(i,j) dp[i][j]=min(dp[i−1][j−1],dp[i−1][j],dp[i][j−1])+dis(i,j)。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL MAXN=1005;
LL n,m;
LL fx,fy,bx,by;
char a[MAXN],b[MAXN];
LL f[MAXN][MAXN];
LL F[MAXN][2],B[MAXN][2];
LL dis(LL i,LL j){
LL d1=(F[i][0]-B[j][0]);
LL d2=(F[i][1]-B[j][1]);
LL d=d1*d1+d2*d2;
return d;
}
int main(){
scanf("%lld%lld",&n,&m);
scanf("%lld%lld%lld%lld",&fx,&fy,&bx,&by);
for(LL i=1;i<=n;i++){
scanf("%c",&a[i]);
while(1){
if(a[i]>='A'&&a[i]<='Z')break;
scanf("%c",&a[i]);
}
}
for(LL i=1;i<=m;i++){
scanf("%c",&b[i]);
while(1){
if(b[i]>='A'&&b[i]<='Z')break;
scanf("%c",&b[i]);
}
}
F[0][0]=fx;F[0][1]=fy;
for(LL i=1;i<=n;i++){
if(a[i]=='N'){
F[i][0]=F[i-1][0];
F[i][1]=F[i-1][1]+1;
}else if(a[i]=='E'){
F[i][0]=F[i-1][0]+1;
F[i][1]=F[i-1][1];
}else if(a[i]=='S'){
F[i][0]=F[i-1][0];
F[i][1]=F[i-1][1]-1;
}else{
F[i][0]=F[i-1][0]-1;
F[i][1]=F[i-1][1];
}
}
B[0][0]=bx;B[0][1]=by;
for(LL i=1;i<=m;i++){
if(b[i]=='N'){
B[i][0]=B[i-1][0];
B[i][1]=B[i-1][1]+1;
}else if(b[i]=='E'){
B[i][0]=B[i-1][0]+1;
B[i][1]=B[i-1][1];
}else if(b[i]=='S'){
B[i][0]=B[i-1][0];
B[i][1]=B[i-1][1]-1;
}else{
B[i][0]=B[i-1][0]-1;
B[i][1]=B[i-1][1];
}
}
for(LL i=0;i<=n;i++){
for(LL j=0;j<=m;j++){
f[i][j]=LONG_LONG_MAX;
}
}
f[0][0]=0;
for(LL i=0;i<=n;i++){
for(LL j=0;j<=m;j++){
if(i==0&&j==0)continue;
if(i!=0&&j!=0){
f[i][j]=min(f[i][j],f[i-1][j-1]+dis(i,j));
}
if(i!=0){
f[i][j]=min(f[i][j],f[i-1][j]+dis(i,j));
}
if(j!=0){
f[i][j]=min(f[i][j],f[i][j-1]+dis(i,j));
}
}
}
printf("%lld",f[n][m]);
}
T3 关灯
由于数据范围很小,所以可以直接模拟,关键问题在于如何判断奶牛已经能够判断出当前位置。
奶牛判断当前位置会参考两个数据,一是边长,二是角度。由于题目限制,角度只有 90 ° 90° 90°和 270 ° 270° 270°两种。
如果当前奶牛走过的整条路径在整个地图上独一无二,那奶牛就可以判断出当前位置。
思路得到了,但是代码也比较难写,这里提供一种代码较短的写法。(有时候,善用 S T L STL STL会使你的代码更加简洁)
角度可以通过向量叉积判断。而走过的路径可以存储在一个 v e c t o r vector vector里,再用 m a p map map判断是否独一无二。(虽然我也是第一次见把 v e c t o r vector vector放在 m a p map map里,这里数据范围小,可以通过)
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL MAXN=205;
LL x[MAXN],y[MAXN],n;
LL dis[MAXN];
vector<LL>path;
map<vector<LL>,LL>vis;
LL getdis(LL i1,LL i2){
return abs(x[i1]-x[i2])+abs(y[i1]-y[i2]);
}
LL getflg(LL i1,LL i2,LL i3){
LL xx=(x[i2]-x[i1])*(y[i3]-y[i1])-(x[i3]-x[i1])*(y[i2]-y[i1]);
if(xx>0)return 1;
else return 0;
}
int main(){
scanf("%lld",&n);
for(LL i=1;i<=n;i++){
scanf("%lld%lld",&x[i],&y[i]);
}
for(LL i=2;i<=n;i++){
dis[i]=dis[i-1]+getdis(i,i-1);
}
dis[1]=dis[n]+getdis(1,n);
for(LL i=2;i<=n;i++){
path.clear();
for(LL j=i;j<=n;j++){
LL nxt=j+1;
if(nxt==n+1)nxt=1;
LL ang=getflg(j-1,j,nxt);
path.push_back(ang);
vis[path]++;
path.push_back(getdis(j,nxt));
}
}
LL ans=0;
for(LL i=2;i<=n;i++){
path.clear();
for(LL j=i;j<=n;j++){
LL nxt=j+1;
if(nxt==n+1)nxt=1;
LL ang=getflg(j-1,j,nxt);
path.push_back(ang);
if(vis[path]==1||j==n){
ans=max(ans,dis[j]-dis[i]+min(dis[j],dis[1]-dis[j])-min(dis[i],dis[1]-dis[i]));
break;
}
path.push_back(getdis(j,nxt));
}
}
printf("%lld",ans);
}