2021算法竞赛入门班第一节课【枚举、贪心】习题

[USACO 2007 Jan S]保护花朵【经典贪心】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/25043
题解: 很经典的一个贪心问题,你会发现一组牛中相邻的两个牛顺序的改变不会改变其前面的牛的消费,也不会影响后面牛的花费
在这里插入图片描述

设A(t1,d1),B(t2,d2)。前面等的总的时间为s
如果先A再B则总的花费为   s*d1+(s+t1*2)*d2
如果先B再A则总的花费为   s*d2+(s+t2*2)*d1

如果A先回去比B先回去更优则:
s*d1+(s+t1*2)*d2<s*d2+(s+t2*2)*d1
s*d1+s*d2+t1*2*d2<s*d2+s*d1+t2*2*d1
最终化简可得:  t1*d2<+t2*d1
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long int LL;
typedef pair<int,int> PII;
vector<PII>ve;
int n,a,b;
bool cmp(PII a,PII b) {
    
    return a.first*b.second<a.second*b.first;}
int main(void)
{
    
    
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a>>b,ve.push_back({
    
    a,b});
	sort(ve.begin(),ve.end(),cmp);
	LL s=0,ans=0;
	for(int i=0;i<ve.size();i++)
	{
    
    
		ans+=s*ve[i].second;
		s+=ve[i].first*2;
	}
	cout<<ans;
	return 0;
}

[NOIP2005]校门外的树【区间合并】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16649
经典的区间合并,在处理的时候顺便处理计算删除的数量即可。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
vector<PII>ve;
int n,m,l,r;
int main(void)
{
    
    
	cin>>n>>m;
	while(m--) cin>>l>>r,ve.push_back({
    
    l,r});
	sort(ve.begin(),ve.end());
	int l=ve[0].first,r=ve[0].second;
	for(int i=1;i<ve.size();i++)
	{
    
    
		if(ve[i].first<=r) r=max(r,ve[i].second);
		else
		{
    
    
			n-=r-l+1;
			l=ve[i].first,r=ve[i].second;
		}
	}
	n-=r-l+1;
	cout<<n+1<<endl;//+1是因为从0开始
	return 0;
}

差分做法: 对于删除的区间统一加1,最后统计0的个数即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int s[N],n,m,l,r; 
int main(void)
{
    
    
	cin>>n>>m;
	while(m--)
	{
    
    
		cin>>l>>r;
		l++,r++;//从将其 [0,n] 变成  [1,n+1]
		s[l]+=1,s[r+1]-=1;
	}
	int ans=0;
	for(int i=1;i<=n+1;i++) 
	{
    
    
		s[i]=s[i]+s[i-1];
		if(!s[i]) ans++;//当前的数为0,说明该树还在
	}
	cout<<ans;
	return 0;
}

[NOIP2006]明明的随机数【签到】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16669

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],n,x,cnt;
int main(void)
{
    
    
	cin>>n;
	for(int i=0;i<n;i++) 
	{
    
    
		cin>>x;
		if(!a[x]) cnt++;
		a[x]++;
	}
	cout<<cnt<<endl; 
	for(int i=0;i<N;i++) if(a[i]) cout<<i<<" ";
	return 0;
}

[HNOI2003]激光炸弹【二维前缀和】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/20032

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int s[N][N],n,m;
int main(void)
{
    
    
	cin>>n>>m; m=min(m,N);
	while(n--)
	{
    
    
		int x,y,c; cin>>x>>y>>c;
		x++,y++;
		s[x][y]+=c;
	}
	for(int i=1;i<N;i++)
		for(int j=1;j<N;j++)
			s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
	int res=0;
	for(int i=1;i<N-m;i++)
		for(int j=1;j<N-m;j++)
		{
    
    
			int x=i,y=j;
			int xx=i+m-1,yy=j+m-1;
			int temp=s[xx][yy]-s[x-1][yy]-s[xx][y-1]+s[x-1][y-1];
			res=max(res,temp); 
		}
	cout<<res<<endl;
	return 0;
}

铺地毯【枚举】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16593
题解: 先存一下所有的地毯,然后枚举所有的地毯可不可以覆盖主该点即可。

#include<bits/stdc++.h>
using namespace std;
struct node{
    
    int x,y,g,k;}temp;
vector<node>ve;
int n,x,y;
int main(void)
{
    
    
	cin>>n;
	while(n--) 
	{
    
    
		cin>>temp.x>>temp.y>>temp.g>>temp.k;
		ve.push_back(temp);
	}
	cin>>x>>y;
	int ans=-1;
	for(int i=0;i<ve.size();i++)
	{
    
    
		int l1=ve[i].x,r1=l1+ve[i].g-1;
		int l2=ve[i].y,r2=l2+ve[i].k-1;
		if(x>=l1&&x<=r1&&y>=l2&&y<=r2) ans=i+1;
	}
	cout<<ans;
}

[NOIP2007]纪念品分组【模拟】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16640
就用双端队列模拟即可,每次取一个最大的和一个最小的看可不可以配对,尽可能多的配对。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int m,n,a[N];
deque<int>q; 
int main(void)
{
    
    
	cin>>m>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	sort(a,a+n);
	for(int i=0;i<n;i++) q.push_back(a[i]);
	int cnt=0;
	while(q.size())
	{
    
    
		if(q.size()>=2)
		{
    
    
			auto l=q.front(); q.pop_front();
			auto r=q.back();  q.pop_back();
			if(l+r>m) q.push_front(l);
		}else q.pop_back();
		cnt++;
	}
	cout<<cnt<<endl;
	return 0;
}

[NOIP2016]回文日期【日期模拟】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16438

#include<bits/stdc++.h>
using namespace std;
int month[13]={
    
    0,31,28,31,30,31,30,31,31,30,31,30,31};
bool judge(int x)
{
    
    
	if(x%400==0||(x%4==0&&x%100!=0)) return true;
	return false;
}
bool check(int a,int b,int c)
{
    
    
	string s1=to_string(a);
	while(s1.size()<4) s1="0"+s1;
	string s2=to_string(c);
	while(s2.size()<2) s2="0"+s2;
	s2=to_string(b) +s2;
	while(s2.size()<4) s2="0"+s2;
	string s=s1+s2;
	for(int i=0;i<8/2;i++) if(s[i]!=s[8-1-i]) return false;
	return true;
}
int main(void)
{
    
    
	int year1,year2,month1,month2,day1,day2;
	string a,b; cin>>a>>b;
	year1=stoi(a.substr(0,4)),month1=stoi(a.substr(4,2)),day1=stoi(a.substr(6,2));
	year2=stoi(b.substr(0,4)),month2=stoi(b.substr(4,2)),day2=stoi(b.substr(6,2));
	int cnt=0;
	if(check(year1,month1,day1)) cnt++;
	while( (year1!=year2) || (month1!=month2) || (day1!=day2) )
	{
    
    
		day1++;
		if(judge(year1)) month[2]=29;
		if(day1>month[month1]) day1=1,month1++;
		if(month1>12) month1=1,year1++;
		if(check(year1,month1,day1)) cnt++;
		month[2]=28;
	}
	cout<<cnt;
	return 0;
}

[NOIP1998]拼数【经典贪心题】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16783

#include<bits/stdc++.h>
using namespace std;
string s;
vector<string>ve;
bool cmp(string a,string b) {
    
    return a+b>b+a;}
int main(void)
{
    
    
	int n; cin>>n;
	while(n--) cin>>s,ve.push_back(s);
	sort(ve.begin(),ve.end(),cmp);
	for(int i=0;i<ve.size();i++) cout<<ve[i];
}

毒瘤xor【贪心】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/18979

以第一个例子为例
0000,0000,0000,0000,0000,0000,0100 1110
0000,0000,0000,0000,0000,0000,0000,1100
0000,0000,0000,0000,0000,0000,0000,0001
0000,0000,0000,0000,0000,0000,0000,0011
我们对于每一位看是0优还是1优既可以了,这里的优主要看该位的哪个数更少。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int s[N][32];
int n,m,x;
int main(void)
{
    
    
	cin>>n;
	for(int i=1;i<=n;i++)
	{
    
    
		cin>>x;
		for(int j=0;j<31;j++)
			if((x>>j)&1) s[i][j]++;
	}
	for(int i=1;i<=n;i++)//前缀和
		for(int j=0;j<31;j++) s[i][j]+=s[i-1][j];
	cin>>m;
	while(m--)
	{
    
    
		int l,r; cin>>l>>r;
		int ans=0;
		int temp[32]={
    
    0};
		for(int i=0;i<31;i++) temp[i]=s[r][i]-s[l-1][i];//区间
		for(int i=0;i<31;i++)
		{
    
    
			int cnt0=r-l+1-temp[i];//该位0的个数
			int cnt1=temp[i];//该位1的个数
			if(cnt0>cnt1) ans+=pow(2,i);//贪心
		}
		cout<<ans<<endl;
	}
	return 0;
}

字符串【双指针】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/18386

#include<bits/stdc++.h>
using namespace std;
int cnt[29];
int main(void)
{
    
    
	string s; cin>>s;
	int ans=1e9,n=0;
	for(int i=0,j=0;i<s.size();i++)
	{
    
    
		if(!cnt[s[i]-'a']) n++;
		cnt[s[i]-'a']++;
		while(n==26&&cnt[s[j]-'a']>1)  cnt[s[j]-'a']--,j++;
		if(n==26) ans=min(ans,i-j+1);
	}
	cout<<ans;
	return 0;
}

数学考试【贪心 前缀和】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/15553
枚举第二个区间,在枚举第二个区间的时候,维护前面的那个区间的值最大。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5*2+10;
typedef long long int LL;
LL s[N],t,n,k;
int main(void)
{
    
    
	cin>>t;
	while(t--)
	{
    
    
		cin>>n>>k;
		for(int i=1;i<=n;i++) cin>>s[i],s[i]+=s[i-1];
		LL l=-1e18,ans=-1e18;
		for(int i=k;i+k<=n;i++)
		{
    
    
			l=max(l,s[i]-s[i-k]);
			ans=max(ans,l+s[i+k]-s[i]); 
		}
		cout<<ans<<endl;
	}
	return 0;
}

「土」秘法地震【二维前缀和】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/53676
简单的二维前缀和+枚举

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int s[N][N],n,m,k;
string a[N];
int main(void)
{
    
    
	cin>>n>>m>>k;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			s[i+1][j+1]=a[i][j]-'0';
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)	
			s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
	int cnt=0;	
	for(int i=k;i<=n;i++)
		for(int j=k;j<=m;j++)
			{
    
    
				int x=i-k+1,y=j-k+1;
				int xx=i,yy=j;
				int temp=s[xx][yy]-s[x-1][yy]-s[xx][y-1]+s[x-1][y-1];
				if(temp) cnt++;
			}
	cout<<cnt;
	return 0;
}

丢手绢【取尺法 双指针】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/207040
就是取尺法,用双指针来维护。我们可以知道的是正解一定是趋于总路程对半分的状态,只要维护这个即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,s,a[N];
int main(void)
{
    
    
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i],s+=a[i];
	int ans=0,temp=0;
	for(int i=0,j=0;i<n;i++) 
	{
    
    
		while(temp<s/2)
		{
    
    
			temp+=a[j];
			j=(j+1)%n;
		}
		int val=min(temp,s-temp);
		ans=max(ans,val);
        temp-=a[i];
	}
	cout<<ans<<endl;
	return 0;
}

[NOIP2008]排座椅【贪心】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16618
你会发现的一点就是行列的分法并无联系。故分开考虑即可。
对于每一对的关系,我们就在该行/列记录一下数字即可。最后排序,挨个取最大的即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef pair<int,int> PII;
PII cntx[N],cnty[N];
int n,m,k,l,d;
int main(void)
{
    
    
	cin>>n>>m>>k>>l>>d;
	for(int i=1;i<=m;i++) cntx[i].second=i;//初始化编号
	for(int i=1;i<=n;i++) cnty[i].second=i;//初始化编号
	for(int i=0;i<d;i++)
	{
    
    
		int x,y,xx,yy; cin>>y>>x>>yy>>xx;
		if(abs(x-xx)==1) cntx[min(x,xx)].first++;//该边上的关系数加1
		else cnty[min(y,yy)].first++;
	}
	sort(cntx+1,cntx+1+m);
	sort(cnty+1,cnty+1+n); 
	vector<int>a,b;
	for(int i=n,j=1;j<=k;i--,j++) a.push_back(cnty[i].second);
	for(int i=m,j=1;j<=l;i--,j++) b.push_back(cntx[i].second);
	sort(a.begin(),a.end());
	sort(b.begin(),b.end());
	for(int i=0;i<a.size();i++) cout<<a[i]<<" ";
	cout<<endl;
	for(int j=0;j<b.size();j++) cout<<b[j]<<" ";
	return 0;
}

[NOIP2012]国王的游戏【贪心 高精度】

在这里插入图片描述
https://ac.nowcoder.com/acm/problem/16561
洛谷题解的推导:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
struct node{
    
    long long int x,y;}temp;
vector<node>ve;
bool cmp(node a,node b){
    
    return a.x*a.y<b.x*b.y;}
int n;
vector<int>s,ans;
vector<int> div(vector<int> A,int b)
{
    
    
	vector<int>C;
	int t=0;
	for(int i=A.size()-1;i>=0;i--)
	{
    
    
		t=t*10+A[i];
		C.push_back(t/b);
		t=t%b;
	}
	reverse(C.begin(),C.end());
	while(C.size()>1&&C.back()==0) C.pop_back();
	return C;
}
vector<int> mul(vector<int> A,int b)
{
    
    
	vector<int>C;
	long long 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();
	return C;
}
vector<int> maxv(vector<int> A,vector<int> B)
{
    
    
    if(A.size()>B.size()) return A;
    if(A.size()<B.size()) return B;
    for(int i=A.size()-1;i>=0;i--)
    {
    
    
        if(A[i]>B[i]) return A;
        if(A[i]<B[i]) return B;
    }
    return A;
}
int main(void)
{
    
    
	cin>>n; 
	for(int i=0;i<=n;i++) cin>>temp.x>>temp.y,ve.push_back(temp);
	sort(ve.begin()+1,ve.end(),cmp);
	s.push_back(1),ans.push_back(0);
	for(int i=1;i<=n;i++)
	{
    
    
	    s=mul(s,ve[i-1].x);
		auto temp=div(s,ve[i].y);
		ans=maxv(ans,temp);
	}
	for(int i=ans.size()-1;i>=0;i--) cout<<ans[i];
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46527915/article/details/121736437