算法竞赛入门——贪心

拼数

题目描述
设有n个正整数(n ≤ 20),将它们联接成一排,组成一个最大的多位整数。
例如:n=3时,3个整数13,312,343联接成的最大整数为:34331213
又如:n=4时,4个整数7,13,4,246联接成的最大整数为:7424613

输入描述:

第一行,一个正整数n。
第二行,n个正整数。

输出描述:

一个正整数,表示最大的整数

示例一

输入

3
13 312 343

输出

34331213

分析
本题由n个正整数拼成一个多位整数,结果可能超过long long,可以将整数看成字符串读入。接下来就是对字符串进行排序,先考虑对于两个字符串a,b如何进行排序。对于字符串a,b,需要自定义排序规则,如果a+b>b+a,就把a排在b的前面,反之则把a排在b的后面,最后将所有字符串按序输出,即可得到最大的整数。注意不能直接比较a,b的大小决定哪个放在前,例如a=‘321’,b=‘32’,a>b,但32132<32321,此时应该b放在a前。

AC代码

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

bool cmp(string a,string b)
{
    
    
    return a+b>b+a;
}

int main()
{
    
    
    int n;
    cin>>n;
    string s[n];
    for(int i=0;i<n;i++)
        cin>>s[i];
    sort(s,s+n,cmp);
    for(int i=0;i<n;i++)
        cout<<s[i];
    cout<<endl;
    
    return 0;
}

矩阵消除游戏

在这里插入图片描述
示例1

输入

3 3 2
101 1 102
1 202 1
100 8 100

输出

414

备注:

在这里插入图片描述
分析
按行枚举,用二进制数表示每行是否选择,当确定选择哪几行之后,就从列中贪心地选择最大的几列。当k>=n或k>=m时,就能取遍所有元素,直接输出元素的和即可。在枚举行的时候,要注意枚举的方案是否可行。

AC代码

#include <bits/stdc++.h>

using namespace std;
const int N=20;
typedef long long ll;
int n,m,k;
int a[N][N];
ll r[N]; //存放每一行的和 
ll c[N]; //存放每一列的和(在行选择之后)
bool b[N]; //存放每一行选不选的状态 
int solve(int n)   //将整数转化为二进制数,并返回1的个数
{
    
    
	int cnt=0;
	int i=1;
	memset(b,0,sizeof(b));
	while(n)
	{
    
    
		if(n&1)
		{
    
    
			b[i]=1;
			cnt++;
		}
		i++;
		n>>=1;
	}
	
	return cnt;
}

int main()
{
    
    
	ll ans=0;  //最大分数 
	ll sum=0;  //所有元素之和 
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=1;j<=m;j++)
		{
    
    
			scanf("%d",&a[i][j]);
			r[i]+=a[i][j];
			sum+=a[i][j];
		}
	}
	if(k>=n||k>=m) 
	{
    
    
		printf("%lld\n",sum);
	}
	else
	{
    
    
		for(int i=0;i<(1<<n);i++)
		{
    
    
			int num=solve(i);
			if(k-num<0||k-num>m) continue;
			else
			{
    
    
				sum=0;  //此处sum存放当前方案的最大值 
				memset(c,0,sizeof(c));
				for(int j=1;j<=n;j++)
				{
    
    
					if(b[j])  sum+=r[j];
				}
				for(int u=1;u<=n;u++)
				{
    
    
					for(int v=1;v<=m;v++)
					{
    
    
						if(!b[u]) c[v]+=a[u][v];
					}
				}
				sort(c+1,c+m+1,greater<int>());   //从大到小排序
				for(int j=1;j<=k-num;j++)
					sum+=c[j];
				ans=max(ans,sum);
			}
		}
		printf("%lld\n",ans);
	}
	
	return 0;
}

注意
本题代码实现的过程,有很多细节需要注意。首先是数组的初始化,当枚举一种新的状态时,数组需要初始化。其次是判断枚举的方案是否合法,即枚举的方案要满足0<=k-num<=m,如果不满足,则继续枚举下一种方案。

国王的游戏

题目描述
恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

输入描述:

第一行包含一个整数 n ,表示大臣的人数。
第二行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n 行,每行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。

输出描述

一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。

示例1

输入

3
1 1
2 3
7 4
4 6

输出

2

说明

按 1 、 2 、 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 1 、 3 、 2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 2 、 1 、 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 2 、 3 、 1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 ;
按 3 、 1 、 2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 ;
按 3 、 2 、 1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 。
因此,奖赏最多的大臣最少获得 2 个金币,答案输出 2 。

备注:

对于 20%的数据,有 1≤ n≤ 10,0 < a,b < 8 ;
对于 40%的数据,有 1≤ n≤20,0 <a,b<8 ;
对于 60%的数据,有 1≤ n≤100 ;
对于 60%的数据,保证答案不超过 109 ;
对于 100%的数据,有 1 ≤ n ≤1,000,0 < a,b < 10000 。

分析
对两个相邻的大臣A,B,确定其排序的先后顺序。首先A,B的顺序不影响A,B前后大臣所获得的金币数,设A,B之前左手的金币的乘积为S,分两种情况讨论:
A排在B前,有A的金币数①S/Ra,B的金币数②S×La/Rb
B排在A前,有A的金币数③S×Lb/Ra,B的金币数④S/Rb
要使A排在B前,则需满足max(①,②)<max(③,④)
观察上式,③>①,②>④,要满足max(①,②)<max(③,④),只需②<③
可得La/Rb<Lb/Ra
即La×Ra<Lb×Rb

AC代码

C++

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

using namespace std;

struct node {
    
    
	int l,r;
}p[1005];

vector<int> C;
vector<int> val;
bool cmp(const node&A,const node&B)
{
    
    
	return A.l*A.r<B.l*B.r;
}

bool operator>(vector<int> &A,vector<int> &B)
{
    
    
	if(A.size()!=B.size()) return A.size()>B.size();
	for(int i=A.size()-1;i>=0;i--)
		if(A[i]!=B[i])
			return A[i]>B[i];
	return false;
}

void mul(vector<int> &A,int b)
{
    
    
	C.clear();
	int t=0;
	for(int i=0;i<A.size()||t;i++)
	{
    
    
		if(i<A.size()) t+=A[i]*b;
		C.push_back(t%10);
		t/=10;
	}
	while(C.size()>1&&C.back()==0) C.pop_back(); //去掉前导0
}

void div(vector<int> &C,int b)
{
    
    
	val.clear();
	int r=0;
	for(int i=C.size()-1;i>=0;i--)
	{
    
    
		r=r*10+C[i];
		val.push_back(r/b);
		r%=b;
	}
	reverse(val.begin(),val.end());
	while(val.size()>1&&val.back()==0) val.pop_back();  //去掉前导0
}

int main()
{
    
    
	int n;
	int a,b;
	cin>>n;
	cin>>a>>b;
	for(int i=0;i<n;i++)
		cin>>p[i].l>>p[i].r;
	sort(p,p+n,cmp);
	vector<int> A;
	int x=a;
	while(x)
	{
    
    
		A.push_back(x%10);
		x/=10;
	}
	mul(A,1);
	vector<int>ans;
	for(int i=0;i<n;i++)
	{
    
    
		div(C,p[i].r);
		if(ans.size()==0||val>ans)
			ans=val;
		A.assign(C.begin(),C.end());
		mul(A,p[i].l);
	}
	for(int i=ans.size()-1;i>=0;i--)
		cout<<ans[i];
	cout<<endl;
	
	return 0;
}




Python

n=int(input())
a,b=map(int,input().split())
ret=[]
for i in range(n):
    temp=list(map(int,input().split()))
    ret.append([temp[0],temp[1]])
ret.sort(key = lambda x:x[0]*x[1])
ans=0
for i in ret:
    if a//i[1]>ans:
         ans=a//i[1]
    a*=i[0]
print(ans)

注意
1.此题数据结果过大,用C++编写需要使用高精度,也可以使用Python
2.自定义排序规则La×Ra<Lb×Rb保证A,B获得的金币的最大值最小,不能得出A的金币数小于B的金币数

猜你喜欢

转载自blog.csdn.net/weixin_46155777/article/details/112757027
今日推荐