贪心法(洛谷普及场2)

17 篇文章 0 订阅
10 篇文章 0 订阅

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
P1031 均分纸牌
题目链接:https://www.luogu.org/problemnew/show/P1031
解法一:直接模拟(能求出过程)
解法二,贪心
A[i]为减去a[i]平均数的值
sum[i]为A前缀和
从做至右当sum[i]<0时说明第i位不够,需要i+1位左移动abs(sum[i])张,步数加1
当sum[i]>0时说明第i位多了,需要向i+1位左移动abs(sum[i])张,步数加1
此时i已经到达平均数了,从左至右依次执行操作,i的左边都是已经到达平均数的,i是需要调整的,i的右边是用来调整i的
当A[i+1]往左移动不够abs(sum[i])张时候,可以理解为先向后面的借了不够的张数
借的张数会在计算i+1这一位sum[i+1]体现出来及时借过来,向i+2借(虽然i+2也可能时借的),但往后面总有一个是不用借的

#include<bits/stdc++.h>
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,a[200],sum[200];
// 模拟过程 
void solve(){
	fo(i,1,n)sum[i]=sum[i-1]+a[i]; 
	int ave = sum[n]/n;  
    int count=0,d; 
    for(int i=1; i<=n; i++)
    {
        if(sum[i]>i*ave) //sumstd[i]=i*ave, 向后面均匀一些 
        {
            d = sum[i]-i*ave;
            sum[i] -= d;
            a[i] -= d;
            a[i+1] += d;
            count++;
        }                    
    }
    for(int i=n; i>=1; i--)
    {
        if(a[i]>ave) //向前面均匀一些 
        {
            d = a[i]-ave;
            a[i] -= d;
            a[i-1] += d;
            sum[i-1] += d;
            count++;
        }                    
    }
    cout << count;
}
int main(){
	scanf("%d",&n);
	int all=0;
	fo(i,1,n){
		scanf("%d",&a[i]);
		all+=a[i];
	}
	int k = all/n, ans=0;
	fo(i,1,n){
		sum[i] = sum[i-1] + a[i]-k;
		ans += abs(sum[i])==0?0:1;// i左边已经平均,即都为0,查看sum[i]是否多了或者少了,多了往右移动,少了右边往左移动 
	}
	printf("%d\n",ans);
} 

P1233 木棍加工
题目链接:https://www.luogu.org/problemnew/show/P1233
法一:贪心,在宽度排序好的情况下,下降排序 ,求最少有多少个长度递减序列
法二:DP,如果是求下降子序列的最小划分,相当于是求最小反链划分,等于最长不下降子序列的长度。 最长严格上升序列长度
最长严格上升序列长度 ,DP做法(慢),二分做法(nlogn快,每次查找出新数组1~len范围内大于等于要插如的数的下标,然后插入

#include<bits/stdc++.h>
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
struct Node{
	int l,w;
	bool operator < (const Node & x)const{
		if(w==x.w){
			return l>=x.l;
		}else return w>=x.w;
	}
}a[5005]; 
int n;
bool vis[5050];
// 40ms 贪心扫ans遍 
void solve(){
	sort(a+1,a+1+n); // 宽度已经排序好,下降排序 
//	fo(i,1,n){
//		printf("%d %d\n",a[i].l,a[i].w);
//	} 
	int ans = 0;
	int l; 
	while(1){
		bool flag=0;
		fo(i,1,n)if(!vis[i]){
			l = a[i].l; // 找到第一个未标记的 
			flag=1;
			break;
		}
		if(!flag)break; // 都标记过了 
		fo(i,1,n){
			if(!vis[i]&&a[i].l<=l){ // 在宽度递增减长度递增排序的情况下,对长度进行贪心选择 
				l = a[i].l;
				vis[i]=1;
			}
		}
		ans++; 
	}
	printf("%d\n",ans);
}
// DP n^2 180ms
int dp[5050];
void solve1(){
	sort(a+1,a+1+n); // 宽度已经排序好,下降排序
	int ans = 0;
	fo(i,1,n){
		dp[i]=1;
		fo(j,1,i){
			if(a[i].l>a[j].l && dp[i]<dp[j]+1){ //求最长严格上升序列长度 
				dp[i] = dp[j]+1;
			}
		}
		if(ans<dp[i])ans=dp[i];
	}
	printf("%d\n",ans);
} 
// nlogn求最长严格上升序列  33 ms 
//用二分的方法在 b[] 数组中找出第一个大于等于 a[i] 的位置并且让a[i] 替代这个位置 
int binary_search(int a[], int l, int r, int x){
	int ans = 1,mid;
	while(l<=r){
		mid = (l+r)>>1;
	//	cout<<mid<<endl;
		if(a[mid]>=x){
			ans = mid;
			r = mid -1;
		}else{
			l = mid +1;
		}
	}
	return ans; // 返回大于等于x的第一个下标	 
} 
int b[5050];
bool cmp(const Node &a, const Node &b){ return a.w>b.w;}
void solve2(){
	sort(a+1,a+1+n); // 宽度已经排序好,下降排序
	int i,len,pos;
	b[1]=a[1].l;
	len=1;
	for(i=2;i<=n;i++){
		if(a[i].l>b[len]){
			b[++len] = a[i].l;
		}else{
			pos = binary_search(b,1,len,a[i].l); // 返回大于等于a[i].l的第一个下标 
		//	b[pos] = min(b[pos],a[i].l);         // 范围一定要为 1,len, 傻傻写了无数遍1-n错了 
			b[pos] = a[i].l;
		}
	}
	printf("%d\n",len);
}
int main(){
//	int c[] = {1,2,3,4,4,5,6,6,6,6,7,10,12};
//	cout<<binary_search(c, 1, 12, 6)<<endl;
	scanf("%d",&n);
	fo(i,1,n){
		scanf("%d%d",&a[i].l,&a[i].w);
	} 
	//solve(); 
	//solve1();
	solve2();
}

P1080 国王游戏
题目链接:https://www.luogu.org/problemnew/show/P1080
解法:相邻两项交换位置对后面的前面和后面没有影响
对左手和有右手乘积进行递增排序,求出过程中最大的结果,证明略

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

const int N = 1e5+5;
struct minister{
	int l,r,rank;
	minister(){}
	minister(int a,int b):l(a),r(b),rank(a*b){}
	bool operator <(const minister &x)const{ return rank<x.rank; }
}a[N];
struct BigInt{
	int data[N];
	BigInt(){
		memset(data, 0, sizeof(data));
		data[0]=1;
	}
	BigInt(int x){//用低精数 初始化(赋值) 高精数 
		memset(data, 0, sizeof(data));
		data[0] = 1;
		int i=1;
		while(x){
			data[i++] = x%10;
			x/=10;
		}
		data[0]=--i;// 这里位数不能错
	}
	BigInt operator *(const int &x){
		BigInt a;
		int len;
		a.data[0] = data[0];
		fo(i,1,data[0])a.data[i] = data[i]*x;
		for(int i=1; i<=a.data[0]||a.data[i]; len = ++i){
			a.data[i+1] += a.data[i]/10;
			a.data[i] %= 10;
		} 
		a.data[len] ? a.data[0] = len : a.data[0] = --len;
		return a;
	}
	BigInt operator /(const int &x){
		BigInt a;
		a.data[0]=data[0];
		int rest = 0;
		for(int i=data[0]; i>=1; i--){ // 注意从高位开始模拟 
			rest = rest*10+data[i];
			a.data[i] = rest/x;
			rest %= x;
		}
		while(!a.data[a.data[0]] && a.data[0]>1) a.data[0]--;// 去掉多余的0
		return a; 
	}
	bool operator <(const BigInt &x)const{
		if(data[0]==x.data[0]){ // 先比较位数 
			int i;
			for(i=data[0]; data[i]==x.data[i] && i>1; i--); // 再比较高位 
			if(i>=1) return data[i]<x.data[i];
			else return false;
		}
		else return data[0]<x.data[0]; 
	}
};

ostream& operator << (ostream &out, const BigInt &x){
	fo(i,1,x.data[0]) out << x.data[x.data[0]-i+1];
	return out;
}
int n,x,y;
BigInt ans;
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	fo(i,0,n){
		cin>>x>>y;
		a[i] = minister(x,y);
	}
	sort(a+1,a+1+n);
	BigInt k(1);
	BigInt temp;
	fo(i,1,n){
		if(a[i-1].l==0)break; // 特判0
		k = k*a[i-1].l;
		// 注意被除数用temp 存下来
        //如有大臣 防止商为0 后 k无法更新
        temp = k/a[i].r;
        if(ans<temp)ans=temp;
	}
	cout<<ans<<endl;
	return 0;
} 

P2123 皇后游戏
题目链接:https://www.luogu.org/problemnew/show/P2123
题解:https://blog.csdn.net/liuzibujian/article/details/81435356

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
    int x,y,d;
    bool operator <(node a) const
    {
        if (d!=a.d) return d<a.d; // 两者的左右关系不同时,先比较d 
        if (d<=0) return x<a.x;   // 本节点左手小于等于右手时,按左手升序 
        return y>a.y;             // 否则按右手降序 
    }
}a[20005];
int t,n;
long long c[20005];
int main()
{
    cin>>t;
    for (int k=1;k<=t;k++)
    {
        cin>>n;
        for (int i=1;i<=n;i++) 
        {
            cin>>a[i].x>>a[i].y;
            if (a[i].x>a[i].y) a[i].d=1; // 左大于右边 
            else if (a[i].x<a[i].y) a[i].d=-1; // 左小于右 
            else a[i].d=0;
        }
        sort(a+1,a+n+1);
        long long s=0;
        for (int i=1;i<=n;i++)
        {
            s+=a[i].x;
            c[i]=max(c[i-1],s)+a[i].y;
        }
        cout<<c[n]<<'\n';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值