贪心算法-CSDN博客

110 阅读2分钟

经典贪心算法

活动安排

n个活动共同使用一个报告厅,给出每个活动的起止时间(整数),如何安排才能使完成活动个数最多?

先开始最先结束的活动,为了留出更多的时间给其他活动。(最多安排活动个数唯一,但活动安排方案可能不唯一)

#include <iostream>
#include <vector> 
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 1e4 + 7;
struct Event {
	int s;
	int e;
	int i;//活动编号 
	Event(int ss, int ee, int ii):s(ss),e(ee),i(ii){};
	Event(){};
	bool operator < (const Event& rhd)const {
		return e < rhd.e;
	} 
};
int main()
{
	int n;
	cin >> n; 
	vector<Event> event;
	for (int i = 0; i < n; ++i) {
		int s, e;
		cin >> s >> e;
		event.push_back(Event(s, e, i));
	}
	sort(event.begin(), event.end());
//	for (vector<Event>::iterator it = event.begin(); it != event.end(); ++it) {
//		cout << "(" << it->s << ", " << it->e << ")" << endl; 
//	}//测试排序函数 
	vector<Event>::iterator it = event.begin();
	//queue<Event> q;
	//q.push(*it);
	Event pre = *it++;
	int cnt = 1;	
	for (; it != event.end(); ++it) {
		if (it->s >= pre.e) {
			//q.push(*it);
			cnt++; 
			pre = *it;
		}
	}
	cout << cnt << endl;
//	while(!q.empty()) {
//		 cout << "(" << q.front().s << ", " << q.front().e << ")" << endl;
//		 q.pop();
//	} //输出其中一种活动安排方案 
	return 0;
}

\

会场安排问题(<-这是测试连接),还是有n个活动,这n个活动必须都举办,问最少需要多少个会场才能满足要求?同样给出n个活动的起止时间(整数)。

小心同一时间既有活动开始,又有活动结束的情况

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
struct Point{
	int time;
	bool fg;
	Point(int time, bool flag):time(time),fg(flag){};
	Point(){};
	bool operator < (const Point& rhd) const {
		if (time < rhd.time)
			return true;//若序化
		if (time == rhd.time)
			return !fg;  //同一时间既有活动开始,又有活动结束,就先结束活动  
		return false;
	}
};
vector<Point> pV; //点的动态数组容器 
int greedy(vector<Point> pV) {
	sort(pV.begin(), pV.end());
	int maxV = 0, cnt = 0; //cnt表示同一时间时在进行的活动的个数 
	for (vector<Point>::iterator it = pV.begin(); it != pV.end(); ++it) {
		if (it->fg) {
			cnt++; 
		} 
		else cnt--;
		maxV = max(maxV, cnt);
	} 
	return maxV; //最多需要几个会场 
}
int main()
{
	int n;
	while(~scanf("%d", &n)) {
		int s, e;
		for (int i= 0; i < n; ++i) {
			scanf("%d%d", &s, &e);
			pV.push_back(Point(s, true));
			pV.push_back(Point(e, false));
		}
		int ans = greedy(pV);
		printf("%d\n", ans); 
		pV.clear();
	}
	
	return 0;
}

百度之星2016的拍照那个问题,和会场安排非常相似,可见基础打好是多么重要。

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 20200;
int n;
struct Node {
	int x, fg, d; //fg = 1, 左端点
	Node(int x = 0, int fg = 0, int d = 0) : x(x), fg(fg), d(d) {}
	bool operator < (const Node &tmp) const	{
		if (x != tmp.x) return x < tmp.x;
		if (fg != tmp.fg) return fg > tmp.fg;
		return d > tmp.d;
	}
}e[N];
int ans[N];
int main()
{  
	int T, cas = 0, x, y, z, d;
	scanf("%d", &T);
	while (T--)	{
		int tot = 0;
		scanf("%d", &n);
		for (int i = 0; i < n; i++) {
			scanf("%d%d%d%d", &x, &y, &z, &d);
			int l = y - z, r = x + z;
			if (l <= r)	{ //在[l,r]可以观测到这条船
				e[tot++] = Node(l, 1, d);
				e[tot++] = Node(r, -1, d);
			}
		}
		sort(e, e + tot);
		int ret = 0, now = ans[tot] = 0;
		for (int i = tot - 1; i >= 0; i--) {//向左方向行驶的船只
			if (e[i].d == -1 && e[i].fg == -1) ++now;
			ans[i] = max(ans[i + 1], now);
			if (e[i].d == -1 && e[i].fg == 1) --now;
		}
		for (int i = 0; i < tot; i++) if (e[i].d == 1) { //向右方向行驶的
				if (e[i].fg == 1) now++;
				ret = max(ret, now + ans[i]);
				if (e[i].fg == -1) now--;
			}
		printf("Case #%d:\n%d\n", ++cas, ret);
	}
	return 0;
}


\

汽车加油问题(我在网上没有找到OJ试题,所以代码没有验证)

一辆汽车加满油后可行驶n公里。旅途中有若干个加油站。设计一个有效算法,指出应
在哪些加油站停靠加油,使沿途加油次数最少。对于给定的n(n <= 5000)和k(k <= 1000)个加油站位置,编程计算最少加油次数。并证明算法能产生一个最优解。
要求:
输入:第一行有2个正整数n和k,表示汽车加满油后可行驶n公里,且旅途中有k个加油站。接下来的1 行中,有k+1 个整数,表示第k个加油站与第k-1 个加油站之间的距离。第0 个加油站表示出发地,汽车已加满油。第k+1 个加油站表示目的地。
输出:输出编程计算出的最少加油次数。如果无法到达目的地,则输出”NoSolution”。

#include <stdio.h>
#include <string.h> 
#define N 10007
int ary[N];
bool vis[N];
int main()
{
	int i, n, k; //加满油行驶k千米,k个加油站,第k + 1个数是目的地 
	while(~scanf("%d%d", &n, &k)) {
		ary[0] = 0; 
		memset(vis, 0, sizeof(vis)); 
		bool fg = true;
		for (i = 1; i <= k + 1; ++i) {
			scanf("%d", ary + i);
			if (ary[i] > n) {
				fg = false;
			}
		} //ary[k + 1]是目的地,ary[i]表示第i-1和第i个加油站间的距离 
		if (fg == false) {
			puts("No Solution");
			continue;
		}
		int cnt = 0, amount = n;
		for (i = 1; i <= k; ++i) { //每次迭代判断第i个加油站是否要停下加油 
			if (ary[i] + ary[i + 1] <= amount) {
				amount -= ary[i];
			}
			else {
				cnt++;
				amount = n;
				vis[i] = true;//在第i个车站停下加油
			}
		}
		printf("%d\n", cnt); 
	}
	
	return 0;
} 


删除数字南京邮电大学OJ1260\

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main()
{
	string str;
	while(cin >> str) {
		int k;
		cin >> k;
		int cnt = str.length() - k; //cnt为最小数的位数 
		string ans;
		string::iterator left = str.begin(), right = str.end() - cnt + 1, it;
		while(cnt--) {
            it = min_element(left, right);
			ans.append(1, *it);
			left = it + 1;
			right++;
		}
		//删除前导0,第三个参数不用写it++,若有删除,it就指向新的字字母了 
		for (it = ans.begin(); it != ans.end() && *it == '0';)
			ans.erase(it);
		if (ans.length() == 0)
			ans = "0";
		cout << ans << endl; 
	}
	

	return 0;
}


\

\


下面的贪心算法无关,就是郑大OJ1003一道题,判断一个(可能超级大的数)数是否是17的倍数。

import java.math.BigInteger;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		BigInteger num, tmp = new BigInteger ("17");
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()) {
			num = sc.nextBigInteger();
			if (num.equals(BigInteger.ZERO))
				break;			
			num = num.mod(tmp);
			if(num.equals(BigInteger.ZERO)){
				System.out.println("1");
			}
			else {
				System.out.println("0");
			}
		}
		sc.close();
	}
}

java / C++

#include <stdio.h>
int main()
{
	char str[105], i;
	while(~scanf(" %s", str) && str[0] != '0') {
		int sum = 0;
		for (i= 0; str[i]; ++i) {
			sum = (sum * 10 + str[i] - '0') % 17;
		} 
		printf("%d\n", (sum == 0 ? 1 : 0));
	} 	
	return 0;
} 



\

\

\

\


\