贪心法(洛谷普及场1)

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
P1090 合并果子
题目链接:https://www.luogu.org/problemnew/show/P1090
题解:每次取最小的两个进行合并

#include
#define fo(i,j,n) for(register int i=j;i<=n;++i)
#define ll long long
using namespace std;
priority_queue<int, vector<int>, greater<int>> pq;
int n,a,b;
int main(){
	scanf("%d",&n);
	fo(i,1,n){
		scanf("%d",&a);
		pq.push(a);	
	}
	ll ans = 0;
	fo(i,1,n-1){
		a=pq.top();pq.pop();
		b=pq.top();pq.pop();
		ans += a+b;
		pq.push(a+b);
	}
	printf("%lld\n", ans);
	return 0;
}

P1181 数列分段Section I
题目链接:https://www.luogu.org/problemnew/show/P1181
解法一:从左到右倍增直到区间不能再增大为止
解法二:贪心,从左到右一直加到不满足为止,然后重新加

#include
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
const int maxn = 1e5+5;
int n,m,a[maxn],sum[maxn];
void solve(){
	int l=1,p=1,r=l,ans=0;
	while(l<=n){
		ans++;
		r=l,p=1;
		while(p){
			if(r+p>n){
				p >>= 1;continue;
			}
			if(sum[r+p]-sum[l-1]<=m){
				r += p;
				p <<= 1;
			}else{
				p >>= 1;
			}
		}
		l = r+1;
	//	cout<
	}
	printf("%d\n",ans);
}
void solve1(){
	int ans=1,s=0,x;
	fo(i,1,n){
		scanf("%d",&x);
		if(s+x<=m){
			s+=x;
		}else{
			ans++;
			s=x;
		}
	}
	printf("%d\n",ans);
}
int main(){
	scanf("%d%d",&n,&m);
//	fo(i,1,n)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
//	solve(); // 倍增法,复习一下 
	solve1();// 直接倍增 
	return 0;
}

P1208 [USACO1.3]混合牛奶 Mixing Milk
题目链接:https://www.luogu.org/problemnew/show/P1208
题解:每次选便宜的牛奶购买

#include
#define ll long long 
#define mk make_pair
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,m;
vector<pair<int,int> > ver; 
void solve(){
	sort(ver.begin(),ver.end());
	ll ans=0,all=0;
	for(auto p:ver){
		if(all+p.second<=n){
			ans += p.first*p.second;
			all += p.second;
		}else{
			ans += (n-all)*p.first;
			break;
		}
	}
	printf("%lld\n",ans);
} 
int main(){
	scanf("%d%d",&n,&m);
	int a,b;
	fo(i,1,m){
		scanf("%d%d",&a,&b);
		ver.push_back(mk(a,b));
	}
	solve();
	return 0;
}

P1223 排队接水
题目链接:https://www.luogu.org/problemnew/show/P1223
题解:
微扰法证明:
要使总等待时间最少,每一次接水的是还没接水的人中(m人)接水时间t1最少的
这时的后面的人的总等待时间为(m-1)t1,由于ti最小,则该局面下的总等待时间最小
设后面的某个人的等待时间为tk(tk>t1),排第k位(k>1)
这后面的人等待该人的总等待时间为 tk
(m-k)
等待 这两个人的总等待时间为 (m-1)t1+(m-k)tk = m(t1+tk) - t1-ktk = T1
若交换两人位置,等待两人的总等待时间为 (m-1)tk+(m-k)t1 = m(t1+tk) - tk-kt1 = Tk
TK-T1 = -tk - k
t1 + t1 + k
tk = (k-1)*tk - (k-1)t1 = (k-1)(tk-t1)>0
所以任意非位置一(接水时间最少)的位置k,与位置一交换都会使得总等待时间变长
证毕。

#include
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
#define mk make_pair
using namespace std;
int n;
vector<pair<int,int> > ver;
int main(){
	scanf("%d",&n);
	int x;
	fo(i,1,n){
		scanf("%d",&x);
		ver.push_back(mk(x,i));
	}
	sort(ver.begin(),ver.end());//当ti重复时,按照输入顺序即可(sort是可以的)
	double sum=0.0,all=0.0;
	fo(i,0,n-1){
		all+=sum; // 加上ver[i].second,这位同学的等待时间 
		sum+=ver[i].first;// 计算下一位同学的等待时间 
		printf("%d%c",ver[i].second,i==n-1?'\n':' ');
	}
	printf("%.2f\n",all/n);
	return 0;
}

P1094 纪念品分组
题目链接:https://www.luogu.org/problemnew/show/P1094
解法:每次选择最大的那一个看是否有一个能与它配对成功的,显然可能性最大一点是最小的那一个

#include
#define fo(i,j,n) for(register int i=j; i<=n;++i)
using namespace std;
int m,n,a[30005];
int main(){
	scanf("%d%d",&m,&n);
	fo(i,1,n)scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	int ans=0,l=1,r=n;
	while(l<=r){
		if(a[r]+a[l]<=m){
			r--;l++;
			ans++;
		}else{
			r--; ans++;
		}
	}
	printf("%d\n",ans);
}

P1803 凌乱的yyy / 线段覆盖
题目链接:https://www.luogu.org/problemnew/show/P1803
解法:右端点进行排序,每次选择右端点尽量小的,如果该区间可以选择的话,否则继续选择其他右端点尽量小的

#include
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
#define mk make_pair
using namespace std;
int n;
vector<pair<int,int> > ver;
int main(){
	scanf("%d",&n);
	int a,b;
	fo(i,1,n)scanf("%d%d",&a,&b),ver.push_back(mk(b,a));
	sort(ver.begin(),ver.end());
	int ans = 0,last=0;
	fo(i,0,n-1){
		if(ver[i].second>=last){
			last=ver[i].first;
			ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
} 

你可能感兴趣的:(贪心,ACM算法日常)