greedy approach

1.贪心思想引入

找零钱

 【思路】每次找尽可能找最大的零钱数量最多。

#include <bits/stdc++.h>
using namespace std;

#define N 7
const int changes[N] = {100, 50, 20, 10, 5, 2, 1};
int T,V;

int main(){
	scanf("%d", &T);
	while(T--){
		scanf("%d", &V);
		int sum = 0;
		for(int i=0; i<N; i++){
			int cnt = V / changes[i];
			V = V  - cnt * changes[i];
			sum += cnt;
		}
		printf("%d\n", sum);
	}
}

打怪兽

【思路】选择 伤害/血量 的值最大的先打死(为什么不能只看伤害?不够优化,如果一个怪DPS100 HP3  另一个怪DPS99 HP1,如果先打死伤害高的则会造成多失血) 

但是比较函数不能写成 伤害/血量 的形式,因为int会导致值失真,使比较出错,所以采用交叉相乘的形式。

#include <bits/stdc++.h>
using namespace std;

int n;
struct Node{
	int dps;
	int hp;
};
Node node[25];
bool dpsSort(Node n1, Node n2){
	return n1.dps*n2.hp > n2.dps*n1.hp; // 从大到小
}

int main(){
	while(scanf("%d", &n)!=EOF){
		
		for(int i=0; i<n; i++){
			scanf("%d %d", &node[i].dps, &node[i].hp);
		}
		
		sort(node, node+n, dpsSort);
		
		long long heroDamage = 0;
		long long loss = 0;
		for(int i=0; i<n; i++){ // 从攻击最大的开始打
			heroDamage = 0;
			for(int j=i; j<n; j++){ // i之前的全都打死了
				heroDamage += node[j].dps;
			}
			loss += heroDamage * node[i].hp; // 血量为hp的敌人打hp次 每次造成dps点伤害
		}
		
		printf("%lld\n", loss);
	}
}

 问题描述
王鹏是一名新生。他进大学时被要求做一次体检。
现在王鹏到了医院。呃…有这么多学生,而且人数还在增加!
有很多考试科目要做,每个科目都要排队。随着时间的流逝,排队的队伍越来越长。选择排队一直是个问题。请帮王鹏确定一个考试顺序,让他尽早完成所有的体检科目。


输入
这里有几个测试用例。每个测试用例以一行中的正整数n开头,表示主题(队列)的数量。
然后是n行。第i行有一对整数(ai, bi)来描述第i个队列:
1. 如果王鹏在时间0跟随这个队列,王鹏必须等待ai秒才能完成这个主题。
2. 由于排队的队伍越来越长,王鹏不在排队的时候,等待的时间每秒钟会增加bi秒。
输入以n = 0结束。
对于所有的测试用例,0<n≤100000,0≤ai,bi<2^31。


输出
对于每个测试用例,输出一行整数:王鹏完成所有考试科目的最早时间(以秒计)。因为王鹏总是被年份弄糊涂,所以只打印秒模365×24×60×60。

题目大意:体检排队,由于各个科目窗口人数在不停增长,每个科目花费时间不一样,但他想尽快做完所有的,问最少需要多少时间。

        解题思路:由于人数不停增加,而每个科目花费时间不一样,因此,要尽快做增长速度快的那一个,由于每一个花费时间不一样,因此要选择花费时间/等待时间尽量小的那一个科目。按照这种贪心策略去做时间是最少的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
#define ll __int64
const int MAXN = 1e5+10;
const int MOD = 365*24*60*60;
int n;
ll ans;
 
struct point{
    ll a,b;
}p[MAXN];
 
bool cmp(point p1,point p2){
    return p1.a*p2.b<p1.b*p2.a;
}
 
int main(){
    //freopen("input.txt","r",stdin);
    while(~scanf("%d",&n) && n){
        for(int i=0;i<n;++i)
            scanf("%I64d%I64d",&p[i].a,&p[i].b);
        sort(p,p+n,cmp);
        ans=0;
        for(int i=0;i<n;++i){
            ans+=ans*p[i].b+p[i].a;
            ans%=MOD;
        }
        printf("%I64d\n",ans%MOD);
    }
    return 0;
}

2.哈夫曼树

获得哈夫曼树的顶点权重

根据输入字符串创建哈夫曼树,然后获取其顶点值。

#include <bits/stdc++.h>
using namespace std;

long long m;
int t;
char a[10005];
int letter[26];

long long huffman(){
	// 建立小根堆
	priority_queue<int, vector<int>, greater<int>> q;
	for(int i=0; i<26; i++){
		q.push(letter[i]);
	}
//	重复以下步骤,直到优先队列中只剩下一个节点:
//	从优先队列中取出权重最小的两个节点作为左右子节点。
//	创建一个新节点,权重为这两个节点的权重之和,并将其作为父节点连接左右子节点。
//	将新节点放入优先队列中。
//	最终,优先队列中剩下的唯一节点即为Huffman编码树的根节点
	int node1,node2;
	long long w = 0;
	while( q.size()>1 ){
		node1 = q.top();
		q.pop();
		node2 = q.top();
		q.pop();
		q.push(node1+node2);
		w += node1 + node2;
	}
	return w;
}

int main(){
	scanf("%d", &t);
	while(t--){
		memset(letter, 0, sizeof(letter));
		scanf("%lld", &m);
		scanf("%s", a);
		for(int i=0; a[i]!='\0'; i++){
			int index = a[i]-'a';
			letter[index]++;
		}
		long long w = huffman();
		if( w<=m ){
			printf("yes\n");
		}
		else{
			printf("no\n");
		}
	}
}

3.activity selection problem 活动选择问题 

 活动选择问题是一个组合优化(从有限的对象集合中找到最优对象)问题,涉及在给定的时间框架内选择不冲突的活动以执行,给定一组活动,每个活动由开始时间(si)和结束时间(fi)标记。
问题是选择单个人或机器可以执行的活动的最大数量,假设一个人一次只能处理一个活动。
这个问题的一个经典应用是为多个竞争项目安排一个房间,每个项目都有自己的时间要求(开始和结束时间)。

【思路】

1.每次选择结束时间最早的活动

 2.每次选择开始时间最晚的活动

这两种选择都ok

输出n时间之内,最多能看多少节目

 每次选择结束时间最早的节目

#include <bits/stdc++.h>
using namespace std;

struct Channel{
	int begin;
	int end;
};
Channel channel[1005];
int n;
bool compareTime(Channel c1, Channel c2){
	return c1.end < c2.end;
}

int main(){
	while(scanf("%d", &n)!=EOF){
		//TODO
		for(int i=0; i<n; i++){
			scanf("%d %d", &channel[i].begin, &channel[i].end);
		}
		sort(channel, channel+n, compareTime);
		int ans = 1;
		int j = 0;
		for(int i=1; i<n; i++){
			if( channel[i].begin>=channel[j].end ){
				ans++;
				j = i;
			}
		}
		printf("%d\n", ans);
	}
}

 4.The interval coverage problem 区间覆盖问题

给出几段[ai, bi]的区间,用尽量少的区间段覆盖[L, R] 

1. 按ai的升序进行排序
2. 假设被覆盖线段为[L, R],在剩余的区间内,找出其ai小于等于L的最长区间,更新被覆盖线段的[L, R]。
3.重复步骤2

 POJ 1089

写一个程序:
从STD输入中读取一系列间隔,
计算满足上述条件的成对非相交区间,
将计算的间隔以升序写入STD输出

[a;  b] and [c;  d] are in ascending order if, and only if a <= b < c <= d.

【思路】 这道题不同于上面模板,不是要完全覆盖,而是多个间隔拼起来让总长最大,间隔数最小。

#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=50005;

struct node{
    int a;
    int b;
};

node intervals[maxn], ans[maxn]; 

bool cmp(node s, node t)
{
    return s.a<t.a;
}

int main()
{
    int n;
    while(cin>>n)
    {
        int k=0;
        for(int i=0;i<n;i++)
        {
            cin>>intervals[i].a>>intervals[i].b;
        }
        sort(intervals,intervals+n,cmp);
        
        ans[k].a=intervals[0].a;
        ans[k].b=intervals[0].b;
        for(int i=1;i<n;i++)
        {
            if(intervals[i].a<=ans[k].b)
            {
                if(intervals[i].b>ans[k].b)
                ans[k].b=intervals[i].b;
            }
            else
            {
                k++;
                ans[k].a=intervals[i].a;
                ans[k].b=intervals[i].b;
            }
        }
        
        for(int i=0;i<=k;i++)
        {
            cout<<ans[i].a<<" "<<ans[i].b<<'\n';
        }
    }

    return 0;
}

5.Minimum spannding tree 最小生成树

 【思路】树的特点是 边数 = 顶点数 - 1

这道题就是m个顶点,n个边,要求一个最小生成树。最小生成树有加边法(克鲁斯卡尔)和加点法(普利姆)。这里用加边法,每次选择value最小的边(贪心),每加入一条边,为了不构成环,需要利用并查集(两个顶点不在一个集合)。遍历所有边,统计边个数,如果等于定点数-1,则说明构造了一个最小生成树。

#include <bits/stdc++.h>
using namespace std;

int n,m;
struct Edge{
	int head;
	int tail;
	long long value;
};
Edge edge[2000005];
bool cmpVal(Edge e1, Edge e2){
	return e1.value < e2.value;
}
int vexset[100000];
int getSet(int x){ // 并查集
	if( vexset[x]==x )return x;
	else{
		vexset[x] = getSet(vexset[x]);
		return vexset[x];
	}
}

int main(){
	while(scanf("%d %d",&n, &m)!=EOF){
		// n是边数 m是顶点数
		for(int i=0; i<n; i++){
			scanf("%d %d %lld", &edge[i].head, &edge[i].tail, &edge[i].value);
		}
		sort(edge, edge+n, cmpVal);
		// 初始化连通分量数组
		for(int i=1; i<=m; i++){
			vexset[i] = i;
		}
		long long sum =0;
		int total = 0; // 在生成树中的定点数量
		for(int i=0; i<n; i++){
			int v1 = edge[i].head;
			int v2 = edge[i].tail;
			int vset1 = getSet(v1);
			int vset2 = getSet(v2);
			if( vset1!=vset2 ){
				sum+=edge[i].value;
				total++;
				vexset[vset1] = vset2;
				if( total==m-1 )
					break;
			}
		}
		if( total<m-1 ){ // 树的边数 = 顶点数-1
			printf("-1\n");
			
		}
		else{
			printf("%lld\n", sum);
		}
	}
}

6.最短路 

 无向连通有权图——迪杰斯特拉求最短路

 

#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
 
struct Edge{
	int to;
	int value;
};
vector<Edge> edge[1005]; // 二维数组 邻接矩阵
vector< pair<int ,int > > e[maxn];
int n, m, v1, v2;
int dis[maxn];   //dis当前的最短路
int vis[maxn];
 
priority_queue<pair<int, int> > q;
int dijkstra(int s, int t)
{
	while(!q.empty())
	{
		q.pop();
	}
	dis[s]=0;
	q.push(make_pair(-dis[s], s));
	
	while(!q.empty()){
	
		int now=q.top().second;                //一定是最短路上的顶点
		q.pop();
		if(vis[now]){
		
			continue;
		}
		vis[now]=1;
		for(int i=0;i<(int)edge[now].size();i++){       //访问所有的邻接点
		
			int v=edge[now][i].to;
			if(!vis[v] && dis[v]>dis[now]+edge[now][i].value ){
				dis[v]=dis[now]+edge[now][i].value;
				q.push(make_pair(-dis[v], v));//把可能的最短路顶点加入队列
			}
		}
	}
	if(dis[t]==1e9)
	{
		return -1;                            //s-t没有通路
	}
	else
	{
		return dis[t];
	}
	
}
 
int main()
{
	while( scanf("%d %d %d %d",&n, &m, &v1, &v2)!=EOF){
		
		for(int i=0;i<maxn;i++){
			vis[i]=0;
			dis[i]=1e9;
			edge[i].clear();
			
		}
		
		for(int i=0;i<m;i++)
		{
			int x, y, z;
			scanf("%d %d %d",&x,&y,&z);
			edge[x].push_back((Edge){y, z});
			edge[y].push_back((Edge){x, z});
			
		}
		printf("%d\n",dijkstra(v1, v2));
	}
	
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值