一,算法
在当前状态下找出局部最优解,转化为全局最优解。
二,活动安排问题
1.问题描述:给定n个开区间(ai,bi),在给定范围内尽量选择多个区间,使得这些区间两两没有公共点。
2.算法:按照结束时间bi从小到大排序,然后按照排序顺序依次考虑每个活动,如果没有和已选活动冲突,就选,否则不选。
3.代码
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct qing{
int a,b;
bool operator<(const qing &x)const{
return b<x.b;
}
}f[1000001];
int main(){
int n,ans=1,p;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d%d",&f[i].a,&f[i].b);
}
sort(f+1,f+n+1);
p=f[1].b;
for(int i=2;i<=n;i++){
if(f[i].a>=p){
ans++;
p=f[i].b;
}
}
cout<<ans;
}
三,区间选点问题
1.问题描述:给定n个闭区间[ai,bi],在数轴上选尽量少的点,使得第i个区间内至少有vi个点(不同区间内的点可以是同一个)
2.算法:按区间结束位置从小到大排序,依次处理每个区间
统计该区间i内被覆盖的点的个数,如果个数大于vi,则continue;否则从区间末尾开始选择元素并覆盖它。从区间末尾倒推选取元素直到覆盖元素等于vi;
为何从区间末尾开始选择元素?如图,如果选灰色点,移动到黑色点会更优
3…代码
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int f[1000001],ans;//f[i]用于判断第i个点是否被覆盖
struct node{
int s,e,v;
bool operator<(const node &x)const{
return e<x.e;
}
}a[1000001];
int main(){
int h,n;//h无意义,题目具体需求
cin>>h>>n;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i].s,&a[i].e,&a[i].v);
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
int k=0;
for(int j=a[i].s;j<=a[i].e;j++){
if(f[j]) k++;
}
if(k>=a[i].v) continue;
else{
for(int j=a[i].e;j>=a[i].s;j--){
if(!f[j]){
f[j]=1;
k++;
ans++;
if(k==a[i].v) break;
}
}
}
}
cout<<ans;
}
四.区间覆盖问题
1.题目描述:给定n个闭区间[ai,bi],选择最少的区间覆盖一条指定线段[s,t]
2.算法:将所有的区间按左端点从小到大排序,依次处理每个区间,每次选择覆盖点了点s的区间中右端点坐标最大的一个,并将s更新为该区间的右端点坐标,直到选择的区间已经包含了t为止。
例题(喷水装置):长 L 米,宽 W 米的草坪里装有 n 个浇灌喷头。每个喷头都装在草坪中心线上(离两边各 W/2 米)。我们知道每个喷头的位置(离草坪中心线左端的距离),以及它能覆盖到的浇灌范围。
请问:如果要同时浇灌整块草坪,最少需要打开多少个喷头?
3.代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int read() //读入优化
{
char ch=getchar();
int a=0,x=1;
while(ch<'0'||ch>'9')
{
if(ch=='-') x=-x;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
a=(a<<3)+(a<<1)+(ch-'0');
ch=getchar();
}
return a*x;
}
int t,n,l,w;
int pos,R,ans,cnt;
struct water
{
double l,r; //l是装置实际覆盖区间的左端点,r是右端点
}a[15001];
int cmp(water x,water y) //按照左端点从大到小排序
{
return x.l<y.l;
}
int main()
{
t=read(); //t组数据
for(int i=1;i<=t;i++)
{
n=read();
l=read();
w=read();
cnt=0; //cnt是有用的装置的个数,条件为r>w/2
for(int j=1;j<=n;j++)
{
pos=read(); //装置圆心距区间最左端的距离
R=read();
if(R<=w/2) continue;
cnt++;
a[cnt].l=pos-sqrt(R*R-(w/2.0)*(w/2.0));
a[cnt].r=pos+sqrt(R*R-(w/2.0)*(w/2.0));
}
sort(a+1,a+1+cnt,cmp);
double s=0,q; //我们已经将区间覆盖了s米
int k=1,flag=0,ans=0;
while(s<l) //覆盖不满就一直找喷水装置
{
ans++;
q=s;
for(;a[k].l<=q&&k<=cnt;k++) //在s左端找到一个右端点最大的值
if(a[k].r>s) s=a[k].r; //让s更新为最大的r
if(s==q&&s<l) {flag=1;cout<<0<<endl;break;} //判断无解
}
if(flag==0) cout<<ans<<endl;
}
return 0;
}
五.机器加工问题
1.问题描述:有n个产品要在两台机器A,B上加工,每个产品都要先花费时间ai在A上加工,再花费时间bi在B上加工,确定n个产品的加工顺序,使得从第一个产品在A上加工到第n个产品在B上加工为止花费的总时间最短。
2.算法:求一个加工顺序使得加工总时间最短,就是让机器的空闲时间最短。一旦A机器开始加工,则A机器将会不停的进行作业,关键是B机器在加工过程中可能要等待A机器。很明显第一个产品机器在A上加工时,B机器必须要等待,最后一个产品在B上加工时,A机器也必须等待A机器完工。
大胆猜想,要使得机器总空闲时间最短,就要把在A上加工时间最短的产品最先加工,这样使得B机器等待A机器的时间最短,能够在最短的空闲时间内开始加工;把在B机器上加工时间最短的的产品放在最后加工,这样使得A机器等待B完工的时间最短。于是得到贪心策略:
设Mi=min{ai,bi};得到每个产品是在A还是B上的加工时间更短。
将M从小到大排序,然后从第一个开始依次处理,若Mi=ai,则产品i在A上加工时间小于B上加工时间,所以将它排在从头开始的作业后面,若Mi=bio,则产品i在B上的加工时间小于A上的加工时间,所以将它排在从尾开始的作业前面。
举例
(a1, a2, a3, a4, a5)=(3, 5, 8, 7, 10)
(b1, b2, b3, b4, b5)=(6, 2, 1, 4, 9)
则(m1, m2, m3, m4, m5)=(3, 2, 1, 4, 9 )
排序之后为:(m3, m2, m1, m4, m5)
处理m3,因为m3=B3,所以m3安排在后面(,,,,3);
处理m2,因为m2=B2,所以m2安排在后面(,,,2,3);
处理m1,因为m1=A1,所以m1安排在前面(1,,,2,3);
处理m4,因为m4=B4,所以m4安排在后面(1,,4,2,3);
处理m5,因为m5=B5,所以m5安排在后面(1,5,4,2,3)。
从而得到加工的顺序1,5,4,2,3。计算出最短的加工时间34。
3.代码
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n, ans[10001],a[10001],b[10001],m[10001],s[10001];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++){
m[i]=min(a[i],b[i]);
s[i]=i;
}
for(int i=1;i<=n-1;i++){
for(int j=i+1;j<=n;j++){
if(m[i]>m[j]){
swap(m[i],m[j]);
swap(s[i],s[j]);//如果直接sort这一步无法进行,所以选用交换排序
}
}
}
int k=0,t=n+1;
for(int i=1;i<=n;i++){
if(m[i]==a[s[i]]) ans[++k]=s[i];
else ans[--t]=s[i];
}
int t1=0,t2=0;
for(int i=1;i<=n;i++){
t1+=a[ans[i]];
if(t2<t1) t2=t1;
t2+=b[ans[i]];
}
cout<<t2<<endl;
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
}
六.带限期和罚款的单位时间任务调度
1.问题描述:有n个任务,每个任务都需要1个单位时间执行,任务i的截止时间di表示要求任务i在时间di之前必须完成,否则罚款wi。确定所有任务的执行顺序,使得惩罚最少。
2.算法:要使罚款最少,必然优先完成wi值较大的任务,因此,按照wi从大到小排序,依次处理每个任务,若有时间可以安排,则放在最晚的时间点上;否则放在最后的空位上。
为何放在最晚时间点上?
因为越往前的时间点,可以执行的任务数越多,换句话说,越前面的时间越珍贵,为了避免重复,应将当前可执行的任务放在限制时间前最晚的时间点上。
例题:智力大冲浪
三.代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int m,n,f;
bool h[100001];
struct node{
int a,b;//任务期限与罚款金额
bool operator<(const node &x)const{
return b>x.b;
}
}s[1000001];
int main(){
cin>>m>>n;
for(int i=1;i<=n;i++){
scanf("%d",&s[i].a);
}
for(int i=1;i<=n;i++){
scanf("%d",&s[i].b);
}
sort(s+1,s+1+n);
for(int i=1;i<=n;i++){
f=0;
for(int j=s[i].a;j>=1;j--){
if(!h[j]){
f=1;
h[j]=1;
break;
}
}
if(!f) m-=s[i].b;
}
cout<<m;
}
That’s all.