【长春大学ACM协会第二次测试】题解

【长春大学ACM协会第二次测试】题解

本次测试为二分专题测试,考察各位对于二分的理解与运用。

题目分布:第一题为二分acwing的板子题,第二题到第四题为洛谷橙色题目,第五题和第六题分别是洛谷黄色、绿色题目

主要知识点均为二分,除了第五、六题涉及到搜索和并查集。

第一题

因为为板子题,题解省略,具体请自行查询acwing官网上算法基础课中基础算法的二分内容。

链接:https://www.acwing.com/activity/content/problem/content/823/

第二题

根据题目具体内容:由于三个解的分布间隔大于等于1,而且范围已经给出在 − 100 -100 100 100 100 100 之间,因此,数据量足够小,所以只需要对 − 100 -100 100 100 100 100 之间进行遍历,将这些数看做200个间隔,例如 1 − 2 1-2 12 之间为一个间隔,对每个间隔进行二分搜索即可。

#include<iostream>
#include<iomanip>
#include<algorithm>
#include<functional>
#include<numeric>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<map>
#include<cstring>

using namespace std;

const int N = 1;
typedef pair<int,int> PII;
typedef long long ll;

double a,b,c,d;
double fuc(double x){
	return a*x*x*x+b*x*x+c*x+d;
}


int main()
{
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	
	cin>>a>>b>>c>>d;
	
	int cmd=0;
	for(double i=-100;i<=100;i++){
		if(fuc(i)*fuc(i+1)<=0){
			if(fuc(i)==0){
				cout<<fixed<<setprecision(2)<<i<<' ';
				cmd++;
			}
			else if(fuc(i+1)==0){
				cout<<fixed<<setprecision(2)<<i+1<<' ';
				i++;
				cmd++;
			}
			else {
				double l=i,r=i+1;
				double mid;
				while(l<=r&&abs(l-r)>=1e-3){
					mid=(l+r)/2;
					
					if(fuc(mid)*fuc(l)<=0)r=mid;
					else l=mid;
				}
				cout<<fixed<<setprecision(2)<<l<<' ';
				cmd++;
			}
		}
		if(cmd>=3)return 0;
	}

}

注: 第三题和第四题都有简单的数学解法,因为本次考察的是对二分的运行,简单的数学解法在此处并不给出。

第三题

第三题需要我们找到一个最少的血量,保障勇士在闯关的过程中血量不会等于小于0,那么我们直接在一个大区间进行二分搜索,当血量满足成功闯关的条件时,则向左边找,当不满足时向右边找。

#include<bits/stdc++.h>
using namespace std;
int n,a[100005];
bool check(int mid)
{
	int xue=mid;//假设血量
	for(int i=1; i<=n; i++)
	{
		xue+=a[i];//血量加减
		if(xue<=0)//判断机器猫会不会死
		    return false;//会死,返回假,让主函数中假设的血量变多
	}
	return true;//不然让主函数中假设的血量变少
}
int main(){
	cin>>n;
	for(int i=1; i<=n; i++)
	    cin>>a[i];
	int l=1,r=100000000;
	while(l<r)//二分查找
	{
		int mid=(l+r)/2;//假设血量
		if(check(mid))//上面已经说过了
		    r=mid;
		else
		    l=mid+1;
	}//上边是模版,自己记住
	cout<<l;//输出答案
	return 0;
}

第四题

同第三题,也是在一个大的区间内找寻符合条件的答案。如果买的数量使得最后的结果大于等于我们需要的结果时,向左边搜索,找最优解,当不满足时向右找。

#include<bits/stdc++.h>

using namespace std;
const int N = 2e5 + 9;
int n; 
int main() {
    cin >> n;
    int l = 1, r = n;
    auto check = [](int x) {
        int cnt = x, ans = x;
        while (cnt >= 3) {
            int q = cnt / 3;
            ans += q;
            cnt = q + cnt % 3;
        }
        return ans;
    };
    while (l < r) {
        int mid = l + r >> 1;
        if (check(mid) >= n) r = mid;
        else l = mid + 1;
    }
    cout << r << "\n";

}

第五题

这道题涉及到并查集,也可以看做是并查集的板子题,在这里先说并查集的思路。

并查集:先对结点按照时间正序排序,并且根据时间逐步的将每个对应的结点连接,直到所有的结点均链接到一起,此时输出当前的时间。

二分:从 0 0 0 到最大时间(即最后一条公路修复完毕的时间)进行二分搜索,并且check当前时间是否所有的村庄均连接,如果未连接则向右搜索,否则向左搜索,直到找到最后的mid值,并且向右取保障全部的村庄均连接的时间(向右取根据个人代码情况,只要是找到一个时间让所有的村庄刚刚连接完毕即可)。

二分代码:

void dfs(int u)
{
	dist[u]=1;
	for(auto x:g[u])
	{
		if(dist[x]==1) continue;
		dfs(x);
	}
}
bool check(int t)
{
	//cout<<t<<endl;
	for(int i=0;i<=n;i++) {
		dist[i]=0;
	    g[i].clear();
	}
	for(int i=1;i<=m;i++)
	{
		 if(tr[i].time>t) break;
		 int x=tr[i].x,y=tr[i].y;
		 g[x].push_back(y);
		 g[y].push_back(x);
	}
	dfs(1);
	for(int i=1;i<=n;i++)
	if(dist[i]==0) 
	return false;
	return true;
}
void ik_solve()
{
   
   cin>>n>>m;
   for(int i=1;i<=m;i++)
   {
   	int x,y,t;
   	cin>>x>>y>>t;
   	tr[i]={x,y,t};
   }
   sort(tr+1,tr+1+m,cmp);
   int l=0,r=1e6+10;
   while(l<r)
   {
   	int mid=l+r >>1;
   	if(check(mid)) r=mid;
   	else l=mid+1;
   }
   
  // cout<<l<<endl;
   if(!check(l)) cout<<-1;
   else
   cout<<l<<endl;
}
signed mian() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
      int T;
      T=1;
   //   cin>>T;
      while(T--)
      {
       ik_solve();
      }
    return 0;
}

第六题

此题可以用并查集+二分也可以用广搜+二分,在这里给出广搜+二分的思路。

二分的范围可以从 0 0 0 到最大值(该最大值为高度最小的结点和高度最大的结点之间的绝对值差),在这个范围内进行搜索,如果所有的路标都可以在一个 D D D 的值被连接,则向左搜索,反之向右搜索。广搜内容直接套广搜的板子,能够被连接的条件就是两者之间的高度差小于等于 D D D ,同理这也是可以被加入广搜 q u e u e queue queue 的条件。

#include <bits/stdc++.h>
using namespace std;
bool lb[505][505],pd=1,p[505][505];
int n,m,a[505][505],l,r,mid,ans,st,en,tp; 
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};

inline bool bfs() { //直接套广搜板子 
    queue <int> x, y; 
    int sum=1;
    x.push(st);
	y.push(en);
    p[st][en]=1;
    while(!x.empty ()) {
        int xx=x.front(),yy=y.front();
        if(sum==tp) return 1; //所有路标都被包含了就可以返回true了 
        x.pop();
		y.pop();
        for(register int i=0;i<4;i++) {
            int xxx=xx+dx[i];
			int yyy=yy+dy[i];
            if(xxx<1||xxx>n||yyy<1||yyy>m||p[xxx][yyy]||abs(a[xxx][yyy]-a[xx][yy])>mid) continue;
            if(lb[xxx][yyy]==1) sum++; //统计覆盖到的路标 
            x.push(xxx); //入队 
			y.push(yyy);
            p[xxx][yyy]=1;
        }
    }
    return 0;
}

int main () {
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;i++) {
        for(register int j=1;j<=m;j++) {
            scanf("%d",&a[i][j]);
            r=max(r,a[i][j]);
        }
    }
    for(register int i=1;i<=n;i++) {
        for(register int j=1;j<=m;j++) {
            scanf("%d",&lb[i][j]); 
            if(lb[i][j]==1) tp++;
            if(pd==1&&lb[i][j]==1) { //标记第一个路标 
                st=i;
				en=j;
				pd=0;
            }
        } 
    }
    while(l<=r) {
        mid=(l+r)>>1;
        memset(p,0,sizeof p); 
        if(bfs()==true) {
        	r=mid-1;
        	ans=mid;
		}
        else l=mid+1;
    } 
    printf("%d",ans); 
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值