贪心,由局部最优,最终推导出,全部最优。
例题:
每个工作i有两部分组成,第一部分在a中完成,ai,第二部分在b中完成,bi。所有工作需要先完成a再完成b因此,我们要让a时间小的先完成a,然后去完成b,让b时间大的先完成b(也就是说它也要先完成a),这样工作等待时间最少。
那我们怎么选择,a,b的大的跟小的问题呢?
我们开辟一个空间m放,ai与bi的最小值,如果mi与ai相等那么就,将它放在前面完成,如果mi与bi相等,就将它放在后面完成。
具体处理见代码:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int M=1e5;
int a[M],b[M];
struct node
{
int v,s;//v放a,b中最小值,因为要将m升序排列,s放它原本的顺序
}m[M];
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
bool cmp(node a,node b)
{
return a.v<b.v;
}
int main()
{
int n;
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
b[i]=read();
for(int i=1;i<=n;i++)
m[i].v=min(a[i],b[i]),m[i].s=i;
sort(m+1,m+1+n,cmp);
int k=0,tot=n+1;
int ans[M];
for(int i=1;i<=n;i++)
{
if(m[i].v==a[m[i].s])
{
k++;
ans[k]=m[i].s;
}
else
{
tot--;
ans[tot]=m[i].s;
}
}
k=0,tot=0;
for(int i=1;i<=n;i++)
{
k+=a[ans[i]];
if(tot<k) tot=k;
tot+=b[ans[i]];
}
printf("%d\n",tot);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
return 0;
}
题意:选手每个回合答一题,每个题,有限制答题时间,跟权值,如果没时间答这道题,这题的分将被扣去。
题解:
对于每个题i,他的最迟答题时间是ti,我们将所有的题目按照题目权重降序排列,对于要答题,他的答题时间是1-ti,那么它最好的答题时间应该选择在,1-ti之中从左数第一个没有被用过的回合,这样才能保证,其他题有时间答。
代码(代码写的很挫,,下一题跟这题同一类型,下一题的代码更值得借鉴):
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int M=1e5;
int vis[M];
struct node
{
int v,t;
}a[M];
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
bool cmp(node a,node b)
{
if(a.v==b.v)
return a.t<b.t;
return a.v>b.v;
}
int main()
{
int n,m;
m=read();
n=read();
for(int i=1;i<=n;i++)
a[i].t=read();
for(int i=1;i<=n;i++)
a[i].v=read();
sort(a+1,a+1+n,cmp);
int p=1,tim=n;
while(tim>0)
{
p=1;
while((tim>a[p].t||vis[p])&&p<=n)
{
p++;
}
if(p<=n)
{
vis[p]=1;
tim--;
}
else tim--;
}
for(int i=1;i<=n;i++)
if(vis[i]==0)
m-=a[i].v;
printf("%d\n",m);
return 0;
}
由于此题跟上一题题意相同不再解释:
代码(关键在于找它前面第一个没用过的回合,用数组pre标记,每次使用回合后,对pre进行判断更新pre数组):
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
//对于作业按照学分倒序排列后,对于第x天之前需要完成的作业要从x往前找第一个没用过的空间
const int M=1e6+100;
int vis[M];
int pre[M];
struct node
{
int v,t;
}a[M];
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
bool cmp(node a,node b)
{
if(a.v==b.v)
return a.t<b.t;
return a.v>b.v;
}
int find(int x)
{
if(!vis[x])
{
return x;
}
pre[x]=find(pre[x]);
return pre[x];
}
int main()
{
int n,ans=0;
n=read();
for(int i=1;i<=n;i++)
a[i].t=read(),a[i].v=read(),pre[i]=i-1;
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
int tmp=find(a[i].t);
if(tmp)
{
vis[tmp]=1;
ans+=a[i].v;
if(a[i].t!=tmp)
{
pre[a[i].t]=pre[tmp];//关键,如果第i天之前的已经不是这一天那么,我们更新到最新的那一天
}
}
}
printf("%d\n",ans);
return 0;
}
10005. 「一本通 1.1 练习 1」数列极差
题意:对于一个数列,你拿出其中两个值,a,b得到a*b+1再放入数列中,直到数列剩余一个值。按照不同顺序取值,其中剩余值最大的是max,最小的是min,极差是max-min;
题解:
对于一组数我们想,每次我们执行乘操作,那么我们一直拿其中最小的两个,最终结果一定是最大的(因为,最小的经过这样的乘操作后,会很快变大,然后再与大的相乘,就比原本数列中大的相乘得到的值大)
每次拿最大的两个将得到最小的
代码:
#include <iostream>
#include<queue>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define ll long long
//最大的是从最小的开始加,最小的是从最大的开始加
ll read()
{
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
struct cmp1
{
bool operator()(ll &a,ll &b)
{
return a>b;//最小值优先
}
};
struct cmp2
{
bool operator()(ll &a,ll &b)
{
return a<b;//最大值优先
}
};
int main()
{
int n;
priority_queue<ll,vector<ll>,cmp1>p1;
priority_queue<ll,vector<ll>,cmp2>p2;
while(~scanf("%d",&n)&&n)
{
ll x;
for(int i=0;i<n;i++)
{
x=read();
p1.push(x);
p2.push(x);
}
ll maxn,minn;
ll t1,t2;
while(p1.size()>1)
{
t1=p1.top();
p1.pop();
t2=p1.top();
p1.pop();
t1=t1*t2+1;
p1.push(t1);
}
maxn=p1.top();
p1.pop();
while(p2.size()>1)
{
t1=p2.top();
p2.pop();
t2=p2.top();
p2.pop();
t1=t1*t2+1;
p2.push(t1);
}
minn=p2.top();
p2.pop();
printf("%lld\n",maxn-minn);
}
return 0;
}
#10009. 「一本通 1.1 练习 5」钓鱼
题解:
对于五分钟,我们完全可以将五分钟作为进制,就将五分钟的问题化为一分钟:
此题,n最大为100,因此暴力枚举女孩可能走几个鱼塘,然后求出,对于这几个鱼塘,女孩能掉到多少鱼,最后将最大值输出。对于女孩能掉到多少鱼,首先我们将所有可能走过的鱼塘的鱼的初值放入优先队列(降序),然后每次拿第一个,直到剩余时间不足为止。
代码:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int M=1e6+100;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int f[M],d[M],t[M];
int main()
{
priority_queue<pair<int,int> >heap;
int n,T;
n=read(),T=read();
T=T*12;
for(int i=1;i<=n;i++)
f[i]=read();
for(int i=1;i<=n;i++)
d[i]=read();
for(int i=1;i<=n-1;i++)
t[i]=read();
int maxn=-INF;
int time=0;
for(int k=1;k<=n;k++)
{
int rest_time=T-time;
int ans=0;
for(int i=1;i<=k;i++)
heap.push(make_pair(f[i],i));
while(rest_time>0&&heap.top().first>0)
{
pair<int,int>a=heap.top();
heap.pop();
ans+=a.first;
a.first-=d[a.second];
heap.push(a);
rest_time--;
}
if(ans>=maxn)
maxn=ans;
time+=t[k];
}
printf("%d\n",maxn);
return 0;
}