专题训练 - 贪心算法

专题训练 - 贪心算法

比赛链接牛客竞赛:专题训练 - 贪心算法

A - 活动安排

经典的贪心区间问题 本题可以转化成最多不重叠区间覆盖问题,要想使最多,就要对右区间从小到大排序,然后要对左区间进行特判,找出第一个左边界大于等于上一个区间的右边界的区间作为下一个区间,具体代码如下:

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 1e5 + 5;

struct ty{
    int s;
    int f;
};

bool cmp(ty a, ty b){
    return a.f < b.f;
}

void Solve(){
    int n;
    ty hd[1005];
    cin >> n;
    
    for(int i = 0;i < n; i++)  cin >> hd[i].s >> hd[i].f;
    
    sort(hd, hd+n,cmp);
    
    int tmp = hd[0].f, cnt = 1;
    for(int i = 1;i < n; i++){
        if(hd[i].s >= tmp) {
            cnt++;
            tmp = hd[i].f;
        }
    }
    
    cout << cnt;

    return ;
}

int main(){

    int T = 1;
    // cin >> T;

    while(T--){
        Solve();
    }

    return 0;
}

B - 种树

贪心思想,要想尽可能地少种树,就要尽可能在重叠位置种树,才会使此次种树量最少,由A题可以知道,那就是对右端点从小到大排序从后往前种树为最优解(因为假设第一个区间能重叠,那么重叠部分一定是后部分重叠,依次往后推...),至于按照上一区间的右端点大于这一去点的左端点排序会使答案可能变大比如1 6 2 与 2 3 1 这两组数据实际最小是两棵树但按照那个排序会是三棵树,具体代码如下:

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 1e5 + 5;

struct ty {
    int b, e, t;
};

bool cmp(ty a, ty b){
    return a.e < b.e;
}

void Solve(){
    int n, h, used[N], ans = 0;
    ty a[N];
    memset(used,0,N);
    cin >> n >> h;
    
    for(int i = 0; i < h; i++)  cin >> a[i].b >> a[i].e >> a[i].t;
    
    sort(a, a + h, cmp);
    
    for(int i = 0;i < h;i++) {
        int cnt = a[i].t, tmp =a[i].e;
        
        while(tmp >= a[i].b && cnt > 0){
            if(used[tmp] == 1) cnt--;
            tmp--;
        }
        ans += cnt;
        
        if(cnt != 0){
            tmp = a[i].e;
            
            while(cnt > 0){
                if(used[tmp] == 0) {
                    used[tmp] = 1;
                    cnt--;
                }
                tmp--;
            }
        }
    }
    
    cout << ans;


    return ;
}

int main(){

    int T = 1;
    // cin >> T;

    while(T--){
        Solve();
    }

    return 0;
}

C - 喷水装置

PS:看到圆就感觉要长脑子了。。。但幸好想到了每个圆代表的区间其实就是[xr2(w2)2,x+r2(w2)2]
所以问题就转化为:最少能用多少个子区间将总区间表示出来(或者说覆盖)
然后是怎么选区间的问题,只要将每个区间排序,然后贪心地去选就好了。
先排序,每个区间L小在前,然后是R大在前,然后只要我没有达到最大长度,
就选一个区间使得我能到达最远的距离,然后ans++,如果选完n个都无法达到那个长度,
就输出-1,否则输出ans。

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 15005;

struct ty {
    double l;
    double r;
};

bool cmp(ty a, ty b) {
    if (a.l == b.l) return a.r > b.r;
    return a.l < b.l;
}


void Solve(){
    int T, n, l, w;
    ty y[N];
    cin >> T;
    while (T--) {
        cin >> n >> l >> w;
        int tmp = 0;
        for (int i = 1; i <= n; i++) {
            double a, b;
            cin >> a >> b;
            if (2 * b < w) continue;
            y[++tmp].l = a - sqrt(b * b - (w / 2.0) * (w / 2.0));
            y[tmp].r = a + sqrt(b * b - (w / 2.0) * (w / 2.0));
        }

        sort(y + 1, y + tmp + 1, cmp);

        int ans = 0;
        double len = 0, maxr = 0;
        int i = 1;
        while (len < l) {
            maxr = 0;
            while (i <= tmp && y[i].l <= len) {
                maxr = max(maxr, y[i].r);
                i++;
            }
            if (maxr <= len) {
                ans = -1;
                break;
            }
            ans++;
            len = maxr;
        }
        cout << ans << endl;
    }


    return ;
}

int main(){

    int T = 1;
    // cin >> T;

    while(T--){
        Solve();
    }

    return 0;
}

D - 加工生产调度

排序的方法在贪心中经常用到,所以说要熟悉Sort函数的用法

设定每个零件有a,b,pos,分别表示在A车间加工时间,在B车间加工时间,以及编号。
我们比较两个零件的优先级时,加入A应该在B之前加工,那么A.a+B.b+max(A.b,B.a)<B.a+A.b+max(A.a,B.b)
已知min(a,b)+max(a,b)=a+b,那么上式就可以化为min(A.a,B.b)<min(A.b,B.a)
我们以这个规则对所有零件进行排序(如果存在相等的情况,那么优先加工A区间加工时间少的),
问题就解决啦!

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 1005;

struct ty {
    double a;
    double b;
//     double c;
    int pos;
};

bool cmp(ty a, ty b) {
    int i = min(a.a,b.b), j = min(a.b,b.a);
    if(i == j) return a.a < b.a; 
    return i < j;
}

void Solve(){
    int n;
    ty p[N];
    cin >> n;
    for(int i = 1; i <= n; i++)  cin >> p[i].a;
    for(int i = 1; i <= n; i++) {
        cin >> p[i].b;
        p[i].pos = i;
//         p[i].c = p[i].a/p[i].b;
    }
    
    sort(p+1,p+n+1,cmp);
    
//     for(int i = 1; i <= n; i++){
//         cout << p[i].pos;
//         if(i < n) cout << " ";
//     }
    
    int tmp=0, sum=0;//tmp为B车间的工作时间 
    for(int i = 1; i <= n; i++){
        tmp -= p[i].a;
        if(tmp < 0) tmp = 0;
        tmp += p[i].b;
        sum += p[i].a; 
    }
    sum += tmp; //A车间工作完后B车间还需工作tmp 
    
    cout << sum << endl;
    for(int i = 1; i <= n; i++){
        cout << p[i].pos;
        if(i < n) cout << " ";
    }


    return ;
}

int main(){

    int T = 1;
    // cin >> T;

    while(T--){
        Solve();
    }

    return 0;
}

E - 智力大冲浪

首先,先完成会罚款高的游戏明显明显更有益(时间消耗相同),所以,先要对游戏的罚款进行排序(从大到小)。

其次,排完序后,就要考虑这个游戏放在那个时间来做,很明显,我们要先处理罚款大的项,而又尽量不影响后面的游戏,只能将这个游戏放在规定最晚完成的时间段0t的最后面t来做,若后面已经有游戏正在进行,可以考虑t1,直到0,如果还没有对它进行安排,则这个游戏主动放弃0

最后,将放弃的游戏的罚款减去,即为所求解。

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

struct ty{
    int tim;
    int fk;
};

bool camp(ty a, ty b){
    return a.fk > b.fk;
}

void Solve(){
    int m, n, sum = 0;
    ty a[505];
    bool past[505] = {0};
    cin >> m >> n;
    for(int i = 0; i < n; i++) cin >> a[i].tim;
    for(int i = 0; i < n; i++) cin >> a[i].fk;
    
    sort(a, a+n, camp); //根据罚款进行从大到小排序。
//     for(int i = 0; i < n; i++) cout << a[i].tim << " ";
//     cout << endl;
//     for(int i = 0; i < n; i++) cout << a[i].fk << " ";
//     cout << endl;
    
    for(int i = 0; i < n; i++){ //从罚款最大的游戏进行处理。 
        for(int j = a[i].tim; j >= 1; j--){ //从最晚完成的时间判断,若被占领,则向前一步判断。
            if(past[j] == 0){ //代表该时间无游戏占领。 
                past[j] = 1;
                a[i].fk = 0;  //将占领的游戏的罚款变为零,因为按时完成了嘛。 
                break;       //重点,保证一个游戏只占领一个时间。占领后立刻结束循环。
            }
        }
    }
    
    for(int i = 0; i < n; i++) sum += a[i].fk; //统计罚款的总额 
    cout << m - sum << endl;
    return ;
}

int main(){

    int T = 1;
    // cin >> T;

    while(T--){
        Solve();
    }

    return 0;
}

F - 数列极差

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

// struct ty{

// };

// bool cmp(ty a, ty b){


// }


void Solve(){
    int minn, maxn, ans;
	int a[50002];
	int b[50002];
	int i, j;
	int cnt;
	int n, m;
	int asdasd;
	scanf("%d",&n);
	for(i = 0; i < n; i++)
		scanf("%d",&a[i]);
	scanf("%d",&asdasd);
	
	sort(a, a+n);
	
	if(n == 0 && n == 1 && n == 2) printf("0");
	else{
		m = n - 1;
		minn = a[m];
		for(i = 1; i < n; i++) minn = minn*a[m-i] + 1;
		
		for(i = 0;i < n; i++)  b[i] = a[i];
		
		cnt = n - 1;
		maxn = b[1];
		while(cnt > 0){
			maxn = b[1]*b[0] + 1;
			b[0] = maxn;
			if(cnt == 1) break;

			for(j = 1; j < cnt; j++)  b[j] = b[j+1];
			cnt--;
			sort(b, b+cnt+1);
		}

		ans = maxn - minn;

		printf("%d",ans);
	}
	
	return ;
}

int main(){

	int T = 1;
	// cin >> T;

	while(T--){
		Solve();
	}

	return 0;
}

G - 数列分段

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 100001;

// struct ty {



// };

// bool cmp(ty a,ty b){



// }

void Solve(){
int n , m, cnt = 0, sum = 0, a[N];
    cin >> n >> m;
    for(int i=1;i<=n;i++) cin>>a[i];

    for(int i=1;i<=n;i++){
        if((sum+a[i])<=m) sum+=a[i];
        else{
        cnt++;
        sum = a[i];
        }
      if(i == n && sum <= m) cnt++;
    }
    cout << cnt;
    
    return ;
}

int main(){

    int T = 1;
    // cin >> T;

    while(T--){
        Solve();
    }

    return 0;
}

H - 线段

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

struct ty {
    int l;
    int r;
};

bool cmp(ty a, ty b) {
    return a.r < b.r;
}

void Solve() {
    int n, cnt = 1;
    vector<ty> a;
    cin >> n;
    for (int i = 0; i < n; i++) {
        ty temp;
        cin >> temp.l >> temp.r;
        a.push_back(temp);
    }
    sort(a.begin(), a.end(), cmp);
    int k = a[0].r;
    for (int i = 1; i < n; i++) {
        if (a[i].l >= k) {
            cnt++;
            k = a[i].r;
        }
    }

    cout << cnt;
}

int main() {
    int T = 1;
    // cin >> T;

    while (T--) {
        Solve();
    }

    return 0;
}

I - 钓鱼

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 1e5 + 5;
int n, m, ans, f[101][1001], a[101], b[101], t[101]; 

// struct ty {



// };

// bool cmp(ty a,ty b){



// }

void Solve(){
	scanf("%d%d", &n, &m);
	m *= 12;  //有(m*60/5)个5分钟

	for(int i = 1; i <= n; i++) scanf("%d",&a[i]); //第i个湖第一个5分钟能钓到的鱼
	for(int i = 1; i <= n; i++) scanf("%d",&b[i]); //第i个湖每5分钟钓到鱼树木的减少量
	for(int i = 2; i <= n; i++) scanf("%d",&t[i]); //t[i]:从i-1到i的时间

	//f[i][j]:表示走到第i个湖时间为j的最多钓鱼数

	for(int i = 1, tmp = t[1]; i <= n; tmp += t[i+1],i++)  //tmp为从1到i的路程时间
		for(int j = tmp; j <= m; j++){  //枚举到湖i的用时(至少是走到i的时间tmp)
			for(int k = 0; k <= j - tmp; k++) //在i钓鱼的时间
			    f[i][j] = max(f[i][j],f[i-1][j-t[i]-k]+max(0,k*a[i]-(k*(k-1)/2)*b[i]));
				//钓鱼数为a[i]*k-(1+2+3...+(k-1))*b[i]
			ans = max(ans,f[i][j]); //更新答案
		}

	printf("%d",ans);
	return ;
}

int main(){

	int T = 1;
	// cin >> T;

	while(T--){
		Solve();
	}

	return 0;
}

J - 家庭作业

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 1e6 + 5;

bool vis[N]; //标记时间段

struct ty {
    int d; //截止日期
    int f; //学分
};

//按照学分从大到小排序
bool cmp(ty a, ty b) {
    return a.f > b.f;
}

void Solve() {
    int n;
    // 使用 vector 存储 ty 结构体
    vector<ty> a(N);
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i].d >> a[i].f;

    sort(a.begin(), a.end(), cmp);

    int cnt = 0, tim = 0;
    for (int i = 0; i < n; i++) {
        bool flag = 0; //标记当前任务是否已经做过
        if (a[i].d < tim) continue;
        for (int j = a[i].d; j >= 1; j--) {
            if (vis[j] == 0) {
                cnt += a[i].f;
                vis[j] = 1;
                flag = 1;
                break;
            }
        }
        if (!flag) tim = a[i].d;
    }

    cout << cnt;

    return;
}

int main() {
    int T = 1;
    // cin >> T;

    while (T--) {
        Solve();
    }

    return 0;
}

K - 糖果传递

#include <bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

const int N = 1e6 + 5;
// struct ty {



// };

// bool cmp(ty a,ty b){



// }

void Solve(){
	ll n, p = 0;
	vector<long long>a(N, 0);
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		p += a[i];
	}
   
	p /= n;
	for(int i = n; i > 1; i--)
		a[i] = a[i] - p + a[i+1];
	a[1] = 0;
	sort(a.begin()+1, a.begin()+n+1);
//      for(int i = 1; i <= n; i++) cout << a[i] << " ";
//         cout << endl;
	ll ret = 0;
	for(int i = 1; i <= n; i++) ret += abs(a[i] - a[(n+1)/2]);

	cout << ret << endl;

	return ;
}

int main(){

	int T = 1;
	// cin >> T;

	while(T--){
		Solve();
	}

	return 0;
}
posted @   Zyihan_Crz  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示