2022/5/1 HPUACM培训 贪心+二分

A

题意:给出n个节目的起止时间,问最多能完整看多少节目.

思路:贪心

三种想法:

1.根据开始时间对节目排序。但是开始时间早的不一定会被选择,无法判断当前节目该不该选

2.根据节目时长排序。但是短的不一定被选择,无法判断当前节目该不该选,比如:

4
1 10
1 5 
6 10
4 6

3.根据结束时排序。如果选择当前节目,那么它一定是在可以选择的节目中结束时间最早的,可以得到局部最优解,以此类推得到整体最优。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

typedef pair<int, int> pii;

int main() {
	int n;
	while (cin >> n && n) {
		vector<pii> record;
		for (int i = 0; i < n; i++) {
			pii tem;
			scanf("%d %d", &tem.second, &tem.first);
			record.push_back(tem);
		}
		sort(record.begin(), record.end());// 按 节 目 结 束 时 间 排 序
		int count = 0, lastend = -1;// 看 完 整 节 目 数 量 , 上 一 个 节 目 结 束 时 间
		for (int i = 0; i < n; i++) {
			if (record[i].second >= lastend) {
				// 节 目 的 开 始 时 间 一 定 要 在 上 一 个 节 目 结 束 之 后
				count++;
				lastend = record[i].first;
			}
		}
		printf("%d\n", count);
	}
	return 0;
}

B

题意:找满足阶乘有Q个0的最小N

思路:Q个0对应Q个5,找阶乘中有Q个5的最小值

#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
int T;
ll Q, l, r, m, t;
ll search(ll x) //计算x的阶乘中有几个5 
{
    ll cnt = 0;
    while (x)
    {
        cnt += (x / 5);
        x /= 5;
    }
    return cnt;
}
int main()
{
    scanf("%d", &T);
    for (int i = 1; i <= T; i++) //有T组测试用例
    {
        scanf("%lld", &Q);
        l = 1, r = 1e10 + 10; //r的值需要开大一点
        while (l < r)
        {
            m = (l + r) / 2;
            if (search(m) >= Q) //>可能好理解一点,=Q还要r=mid是因为现在碰到的数不一定是最小的满足条件的数,因此还需要再找
            {
                t = m; //储存可行解
                r = m; //r=mid是因为mid可能是满足条件的值,不能跳过
            }
            else
                l = m + 1; //l=mid+1是因为mid不满足条件,直接把左端点移到mid后面一个就星
        }
        printf("Case %d: ", i);
        if (search(t) == Q)
            printf("%lld\n", t);
        else
            printf("impossible\n");
    }
    return 0;
}

C

题意:给出y的值,求使f(x)最小的x

思路:根据导数确定最小值的位置。f'(mid) < 0 则在 mid 右边取最小值, 否则在左边取最小值

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
double check1(double x, double y)
{
    return 6 * pow(x, 7) + 8 * pow(x, 6) + 7 * pow(x, 3) + 5 * x * x - y * x;
}
double check2(double x, double y)
{
    return 42 * pow(x, 6) + 48 * pow(x, 5) + 21 * pow(x, 2) + 10 * x - y;
}
int main() {

    double x;
    int t;
    cin >> t;
    long long y;
    double mid = 0;
    while (t--) {
        scanf("%lld", &y);
        double l = 0, r = 100;
        double ans = 0;
        while (fabs(r - l) > 1e-6) {
            mid = (l + r) / 2;
            if (check2(mid, y) > 0) { r = mid; }
            else { l = mid; }
        }
        printf("%.4lf\n", check1(l, y));
    }
    return 0;

}

D

题意:N个pei分给F+1个人,每个人得到的是每个pei完整的一部分,求每个人最大能分到多少

思路:二分,判断当前大小能不能分成F+1个,通过二分找到最大值

#include<iostream>
#include<algorithm>
#include<cstring>
#define pi acos(-1)
using namespace std;
int T, N, F, R;
double s[20000], x, sum, l, r;
int check(double mid) {
    int z = 0;
    for (int i = 1; i <= N; i++) {
        z += (int)(s[i] / mid);
    }
    if (z >= F) return 1;
    else return 0;

}
int main() {
    cin >> T;
    while (T--) {
        cin >> N >> F;
        F++;
        for (int i = 1; i <= N; i++) {
            cin >> x;
            s[i] = pi * x * x;
            sum += s[i];
        }
        l = 0.0;
        r = sum / F;
        double mid = 0;
        while (fabs(l - r) > 1e-6) {
            mid = (l + r) / 2;
            if (check(mid)) { l = mid; }
            else r = mid;
        }
        printf("%.4lf\n", l);

    }
    return 0;

}

E

题意:每次从字符串的左边或者右边选择较小的一个,组成字典序最小的字符串。

思路:左右不等,选小的。如果左边和右边相等,看起来是任选,但是需要考虑选择哪边的字符可以使较小的字符尽快被选到。

例:

CABBC
先选最后一个:CBBAC
先选第一个:CABBC
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
    int n;
    int num = 0;
    cin >> n;
    char v[2005];
    for (int i = 0; i < n; i++) {
        cin >> v[i];
    }
    int l = 0;
    n--;
    while (l <= n) {
        bool k = false;
        for (int i = 0; i <= n - l; i++) {
            if (v[l + i] < v[n - i]) {
                k = true;//选左边
                num++;
                break;

            }
            else if (v[l + i] > v[n - i]) {
                k = false;//选右边
                num++;
                break;

            }
        }
        if (k)
            cout << v[l++];
        else
            cout << v[n--];
        if (num % 80 == 0)
            cout << endl;

    }
    return 0;
}

G:二分加贪心

题意:一条长为l 的河中有n 块石头,仅可走m 次到河对岸,求所有可行方案中最大跳跃距离的最小值。

思路:设L为跳跃距离的最小值,R为最大值,根据MID是否成立进行区间收缩。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll s[1000000];
ll n,m,l;
ll minn=1000000000;
ll le,ri,ans,mid,cnt;
ll judge(ll a)
{
	cnt=0;
	ll midd=0;//记录中间值 
	for(int i=1;i<=n;i++)
		if(s[i]-s[midd]<=a&&s[i+1]-s[midd]>a)
		{
			midd=i;
			cnt++;
		}
	return ++cnt;//最后一步没有加上 
}
int main()
{
	while(cin>>l>>n>>m)
	{		
		for(int i=1;i<=n;i++) scanf("%lld",&s[i]);//要用scanf,否则TL 
		s[0]=0;
		s[n+1]=l;		
		sort(s,s+n+2);
		ll maxn=0;
		for(int i=1;i<=n+1;i++)
			maxn=max(s[i]-s[i-1],maxn);//最小距离的最小值为两石头相隔距离的最大值 
		le=maxn,ri=l;
		while(le<=ri)
		{
			mid=(le+ri)>>1;
			if(judge(mid)<=m)
			{
				ans=mid;
				ri=mid-1;//求最小距离 
			}
			else le=mid+1;	
		}
		cout<<ans<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值