Hello,大家好!不知不觉就已经到第31天了(昨天比较忙所以没更题解),明天就要蓝桥杯比赛,有些小伙伴可能会像博主这样有考前焦虑症,这很正常,不过要懂得放松和调整状态,要相信:我们都坚持练了31天的题了,啥题没见过?相信自己该会的都已经会了。在此祝愿大家考到自己理想的成绩,全体省一进军国赛!
今日份题解已到达,请小伙伴们接收。
目录
1.马的遍历
解析:
一道BFS模板题,直接套模板就好啦,记得这是中国象棋,马走日(不知道的小伙伴记得去看看中国象棋的规则,身为中国人可不能不会中国象棋啊!)
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000009
using namespace std;
typedef long long LL;
//马走日
const int dr[8]= {2,2,1,1,-2,-2,-1,-1};
const int dc[8]= {1,-1,2,-2,1,-1,2,-2};
int n,m,x,y;
int vis[405][405];
struct node {
int x,y,dis;
node(int x,int y,int dis):x(x),y(y),dis(dis) {}
};
queue<node> q;
void bfs() {
node u(x,y,0);
vis[x][y]=0;
q.push(u);
while(!q.empty()) {
node u=q.front();
q.pop();
for(int i=0; i<8; i++) {
int nx=u.x+dr[i];
int ny=u.y+dc[i];
if(nx<1 || nx>n || ny<1 || ny>m || vis[nx][ny]!=-1) continue;//边界
int diss=u.dis+1;
vis[nx][ny]=diss;
node v(nx,ny,diss);
q.push(v);
}
}
}
int main() {
cin>>n>>m;
cin>>x>>y;
memset(vis,-1,sizeof(vis));//没到达的地方输出-1
bfs();
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++)
printf("%-5d",vis[i][j]);//左顶格,边宽为5
cout<<endl;
}
return 0;
}
2.切绳子
解析:
这是一道浮点二分题。注意输出是两位小数,不用四舍五入。浮点二分while的条件开到1e-8就足够小了,没必要开到1e-12,开到1e-12会T。这里的小数需要截掉,不可以四舍五入,所以我们用sprintf强制将小数点后三位小数转成字符串,然后在第三位小数上加个‘\0’结束符,最后输出字符串。
当然浮点二分不熟的同学可以转成整数来做,将这些数*100就好,两种代码都在下面了。
//浮点二分
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000009
using namespace std;
typedef double D;
int n,k;
D a[10010];
char s[200];
bool check(D x) {
int sum=0;
for(int i=1; i<=n; i++)
sum+=a[i]/x;
return sum>=k;
}
int main() {
cin>>n>>k;
D l=0.0,r=0.0;
for(int i=1; i<=n; i++) {
cin>>a[i];
r+=a[i];
}
while(r-l>1e-8) {
D mid=(l+r)/2.0;
if(check(mid)) {
l=mid;
} else r=mid;
}
sprintf(s+1,"%.3f",l);
s[strlen(s+1)]='\0';
printf("%s",s+1);
return 0;
}
//整数二分
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000009
using namespace std;
typedef long long LL;
int n,k;
double a[10010];//输入是浮点数
LL b[10010];//防止精度溢出
bool check(double x) {
int sum=0;
for(int i=1; i<=n; i++)
sum+=b[i]/x;
return sum>=k;//多了证明每一段小了,往右边
}
int main() {
cin>>n>>k;
double ans;
LL l=0,r=0;//每个*100会让每个数最大值是1e7,再相加肯定爆int,所以要LL
for(int i=1; i<=n; i++) {
cin>>a[i];
b[i]=a[i]*100;//将浮点数转成整数
r+=b[i];
}
while(l<=r) {
double mid=(l+r)/2.0;//浮点数用于极端数据
if(check(mid)) {
ans=mid;
l=mid+1;
} else r=mid-1;
}
//也可以写double s=ans/100,cout<<ans;
printf("%.2f",double(ans/100.0));//除以100.0就可以将其转成浮点数了
return 0;
}
3.导弹拦截
解析:
这是一道最长上升子序列(不上升),可用DP,不过只能过一半数据,还记得前几天的那道“蓝桥骑士”吗?那题和这题一样,也可用DP,不过不能AC,所以这题同理需要用二分+贪心。
这个题的第一问求的是最长不上升子序列,第二问求的是最长上升子序列,理论上来说是可以用lower_bound和upper_bound来做的,只可惜我的功力不足,用了这俩做了12分,所以就手写二分查找了,手写二分查找的道路可谓是非常坎坷啊!
第一问:最长不上升子序列。如果小于等于low中最小的元素就进low,如果大于就找low中第一个小于等于n(需要进low的元素)的位置。由于这个是最长不上升子序列,所以从左到右是从大到小(左边最大,右边最小),第一个小于等于n应该是往小的找(右边),所以我们判定条件时尽量将n放在右边,也就是说low[mid] >= n,mid是中间的位置,n比小于等于的话就在右边,在右边就应该往右边找,也就是l = mid + 1。
第二问:最长上升子序列。如果大于low中最大的元素就进low,如果小于等于的话就找low中第一个大于n(需要进low的元素)的位置。由于这是最长上升子序列(严格单增),所以左小右大,我们需要找第一个大于n的值,应该往大的找(右边),所以我们判定条件时就应该将n置于右边,也就是low[mid] < n,mid是中间的位置,n比它大的话就在右边,在右边也就往右边找,即l = mid+1.
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000009
using namespace std;
typedef long long LL;
int n,low[100010],a[100010];
int ans,cnt;
int main() {
int s=0;
memset(low,0,sizeof(low));
while(scanf("%d",&n)!=EOF) {
a[++s]=n;
}
ans=cnt=1;
low[1]=a[1];
for(int i=2; i<=s; i++) {
if(a[i]<=low[ans]) low[++ans]=a[i];
else {
int l=1,r=ans;
while(l<r) {
int mid=(l+r)/2;
if(low[mid]>=a[i])
l=mid+1;
else r=mid;
}
low[r]=a[i];
}
}
memset(low,0,sizeof(low));
low[1]=a[1];
for(int i=2; i<=s; i++) {
if(a[i]>low[cnt]) low[++cnt]=a[i];
else {
int l=1,r=cnt;
while(l<r) {
int mid=(l+r)/2;
if(low[mid]<a[i]) l=mid+1;
else r=mid;
}
low[r]=a[i];
}
}
cout<<ans<<endl<<cnt;
return 0;
}