贪心
贪心问题往往难在思路的推理和应用,而代码是比较的短的,因此对于此类题目需要进行多次的训练来锻炼自身的思考能力,也要会记录一些好题目来回顾。
P2751 工序安排
题目要求解决工件在两种工序(操作 A 和操作 B)中的最优机器分配问题,以最小化各工序的最大完成时间。具体来说:
- 操作 A 分配:将 N 个工件分配到 M1 台 A 型机器上,每台机器处理工件的时间已知。需使所有工件完成操作 A 的最晚时间(即各机器处理工件的累计时间的最大值)最小。
- 操作 B 分配:在操作 A 完成的基础上,将 N 个工件分配到 M2 台 B 型机器上,每台机器处理工件的时间已知。每个工件的操作 B 必须在其操作 A 完成后开始,需使所有工件完成操作 B 的最晚时间(即各机器处理工件的累计时间与对应操作 A 完成时间的最大值之和的最大值)最小。
最终需输出操作 A 和操作 B 的最小最大完成时间。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MAXN 1010
int a[MAXN],b[MAXN],t[MAXN];
struct node{
int s,v;
};
bool operator <(node x,node y){
return x.s>y.s;
}
priority_queue<node> q;
signed main(){
ios;
int n,n1,n2;
cin>>n>>n1>>n2;
for(int i=1;i<=n1;i++)cin>>a[i];
for(int i=1;i<=n1;i++){
q.push({a[i],a[i]});
}
for(int i=1;i<=n;i++){
t[i]=q.top().s;
int y=q.top().v;
q.pop();
q.push({t[i]+y,y});
}
while(q.size())q.pop();
cout<<t[n]<<" ";
for(int i=1;i<=n2;i++){
cin>>b[i];
q.push({b[i],b[i]});
}
int mx=0;
for(int i=n;i>=1;i--){
auto x=q.top();
q.pop();
mx=max(mx,t[i]+x.s);
x.s+=x.v;
q.push(x);
}
cout<<mx;
return 0;
}
通过最小根堆维护,每次取出最小的元素并将时间更新,最后t为n时的时间即为完成A任务所需要的最小时间,完成B任务时可采取反向最小根堆,从后往前依次进行数据的更新
P3076 [USACO13FEB] Taxi G
描述:长度为m的栅栏上,有n头牛需要坐车前往别的地方,起点和终点分别为a_i和b_i。现在一辆出租车从最左端0出发,要运送完所有牛,最后到达最右端m,求最小路程。出租车只能一次载一只牛。
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MAXN 100010
#define int long long
int s[MAXN],t[MAXN];
signed main(){
ios;
int n,m;
cin>>n>>m;
int ans=0;
for(int i=1;i<=n;i++){
cin>>s[i]>>t[i];
ans+=abs(s[i]-t[i]);
}
n++;
s[n]=m,t[n]=0;
sort(s+1,s+1+n);
sort(t+1,t+1+n);
for(int i=1;i<=n;i++)ans+=abs(s[i]-t[i]);
cout<<ans;
return 0;
}
贪心思路:易知,对于一段有起点和终点的路程,该路程是不能省略的,则需要考虑如何减少司机接单的空载时间,将起点和终点排序,并将0和终点m加入到数组中,易知终点到对应序列的起点的间隔的路程差最短,因为后面的终点肯定比当前的终点更远,现在不用对应起点,后面再用可能不亏但一定不赚,因此贪心思路证明完成
P1248 加工生产调度
使用Johnson算法,Johnson 规则用于解决两台机器的流水作业调度问题,其中每个作业必须先在第一台机器上加工,然后再在第二台机器上加工。在这个问题中,A 车间相当于第一台机器,B 车间相当于第二台机器,产品的 A 时间 a[ i ] 和 B 时间 b[ i ] 分别对应在两台机器上的加工时间。
例题:P1248 加工生产调度
解法:对于产品加工时间a[i]<b[i]的情况,采取a[i]非递减的方式排序,对于 a [ i ] > = b [ i ] a[i]>=b[i] a[i]>=b[i] 的情况,采取 b [ i ] b[i] b[i] 非递减的方式排序
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define int long long
#define MAXN 10010
struct node{
int a,b,c;
};
int a[MAXN],b[MAXN];
bool cmp1(node x,node y){
return x.a<y.a;
}
bool cmp2(node x,node y){
return x.b>y.b;
}
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
vector<node> q,p;
for(int i=1;i<=n;i++)
if(a[i]<=b[i])q.push_back({a[i],b[i],i});
else p.push_back({a[i],b[i],i});
sort(q.begin(),q.end(),cmp1);
sort(p.begin(),p.end(),cmp2);
int ans=0;
int cn1=0,cn2=0;
for(int i=0;i<q.size();i++){
cn1+=q[i].a;
if(cn1>cn2)cn2=cn1;
cn2+=q[i].b;
}
for(int i=0;i<p.size();i++){
cn1+=p[i].a;
if(cn1>cn2)cn2=cn1;
cn2+=p[i].b;
}
cout<<cn2<<endl;
for(int i=0;i<q.size();i++)
cout<<q[i].c<<" ";
for(int i=0;i<p.size();i++)
cout<<p[i].c<<" ";
}
signed main(){
ios;
int t=1;
//cin>>t;
while(t--)
solve();
return 0;
}
CF549G Happy Line
给定一个 n 个元素的整数序列 a, 任意时刻对于任一对相邻元素 (a[i-1],a[i]),若a[i-1] > a[i]则要依次执行如下两个操作:
a[i-1]--,a[i]++
,然后交换 a[i-1] 和 a[i] 的位置。
经过若干次 1、2 操作后,若能使整个序列变成非降的,则输出最终的序列;否则输出 :(
。
1≤n≤2×105,0≤a[i]≤109。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MAXN 1000010
int a[MAXN],b[MAXN];
signed main(){
ios;
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)b[i]=a[i]+i;
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
if(b[i]==b[i-1]){
cout<<":(";
return 0;
}
for(int i=1;i<=n;i++)
cout<<b[i]-i<<" ";
return 0;
}
解法:可以知道,每次交换相邻元素时,前一个元素值-1,下标+1,后一个元素值-1,下标+1,可以知道其元素值加下标的总数是不会变的,可以弄一个b数组记入a[i]+i的值,再按从小到大的次序排序,当有两个b数组的值相同时,不存在一个排列能满足要求,否则,输出b[i]-i的值。