贪心算法
P1223 排队接水
要使得后面等待的时间少,那么就是要尽量让接水时间短的人排在前面。按照接水所需时间从小到大排序,即为接水的顺序。
注意算平均值的时候不要把最后一个人的接水时间也加上。
\(AC code\)
#include<bits/stdc++.h>
using namespace std;
long long a,b[1005],c[1005];
double d;
int main()
{
scanf("%lld",&a);
for(int i=1;i<=a;i++)
{
scanf("%lld",&b[i]);
c[i]=c[i-1]+1;
}
for(int i=1;i<=a;i++)
{
for(int j=1;j<a;j++)
{
if(b[j]>b[j+1])
{
swap(b[j],b[j+1]);
swap(c[j],c[j+1]);
}
}
}
for(int i=1;i<=a;i++)
{
printf("%lld ",c[i]);
}
for(int i=1;i<a;i++)
{
b[i]+=b[i-1];
}
for(int i=1;i<a;i++)
{
b[i]+=b[i-1];
}
d=(double)b[a-1]/a;
printf("\n%.2lf",d);
return 0;
}
P1012 [NOIP1998 提高组] 拼数
第一想法是按从大到小排序拼在一起,但是分析下样例二很快就发现不对(所以貌似白想了好久
设 \(a,b,c,d\) 为字符串。设 \(a>=b\) 当且仅当 \(a+b>=b+a\) 。所以最优排列就是把这几个数按 \(>=\) 排列就好了,因为显然如果其中相邻的两个数不满足前面 \(>=\) 后面,那么把它们两个换一下显然数会更大。不会证明所以就这样吧(逃
\(ACcode\)
#include<bits/stdc++.h>
using namespace std;
string s[25];
int n;
bool cmp(string a,string b){
return a+b>b+a;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) cin>>s[i];
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++) cout<<s[i];
return 0;
}
P1094 [NOIP2007 普及组] 纪念品分组
贪心的方案:
- 如果最大的和最小的能配对,则配成对,否则最大的单独成对,直到全部配对为止。
正确性的证明(个人理解,并不保证严谨):
-
如果该数和最小的数配对的值大于 \(w\) ,那么它和剩下任何数配对的值都一定大于 \(w\) ,因此它只能自己成一组。
-
如果最大的 \(a_r\) 不与最小的 \(a_l\) 配对,而与 \(a_i\) 配对, \(a_l\) 与 \(a_j\) 配对,因为 \(a_l<=a_i\) 且 \(a_r>=a_j\) ,所以交换两者分组不影响后续选择。
-
故此贪心正确。
\(ACcode\)
#include<bits/stdc++.h>
using namespace std;
int n,w;
int a[30001],ans,l,r;
int main()
{
scanf("%d%d",&w,&n);
l=1;r=n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
while(l<=r)
{
if(a[l]+a[r]<=w)
{
l++;r--;ans++;
}
else
{
r--;ans++;
}
}
cout<<ans<<endl;
return 0;
}
P1090 [NOIP2004 提高组] 合并果子
贪心。
每次取最小的两堆合并,最后即为正确答案。(不会证明/wq)
所以说主要问题就是怎么找最小的两堆。
由于中间不断有插入和删除,所以用优先队列。
\(ACcode\)
#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;
int n,a[10001];
long long ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
q.push(a[i]);
}
while(q.size()>=2)
{
int x=q.top();q.pop();
int y=q.top();q.pop();
q.push(x+y);
ans+=x+y;
}
cout<<ans;
}
P1968 美元汇率
受纪念品那题的启发,同一天买入再卖出等于没买。所以问题转化为决定每天是否要买入或卖出。为使最后答案最大,就是要当 a 比较大的时候买进来,比较小的时候卖出去。因此,如果 \(a_i>a_{i+1}\) ,那么在第 \(i\) 天换成马克,在 \(i+1\) 天再换成美元就是赚钱的。以后的每天以此类推,即为最优解。
#include<bits/stdc++.h>
using namespace std;
int n,a[101];
double ans=100;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<n;i++)
{
if(a[i]>a[i+1]) ans=ans*a[i]/a[i+1];
}
printf("%.2lf",ans);
return 0;
}
P1233 木棍加工 (即零件分组)
贪心和 dp 都可以做,这里用的是贪心。
因为要同时考虑 \(l\) 和 \(w\) 两个数据的排序,所以不妨先将 \(l\) 进行升序排序再单独考虑 \(w\) 。
对于每一个还没有被分入其它组的木棍,往后一个个循环,将能放进来的都放进来。最后统计没有加入任何组的木棍数,即为答案。
\(ACcode:\)
#include<bits/stdc++.h>
using namespace std;
int n,now,ans;
struct stick{
int w,l;
}a[5005];
bool cmp(stick x,stick y)
{
if(x.l==y.l) return x.w<y.w;
return x.l<y.l;
}
bool vis[5005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].w);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
now=a[i].w;
ans++;
for(int j=i+1;j<=n;j++)
{
if(a[j].w>=now&&!vis[j])
{
now=a[j].w;
vis[j]=1;
}
}
}
}
cout<<ans<<endl;
return 0;
}
P5761 [NOI1997] 最佳游览
刚开始理解错题意了卡了好久交了四遍才过掉(
因为每一列都可以随便选,所以当然是每一列选最大值。先一遍读入一遍纵向扫出每一列的最大值,储存在 \(a\) 数组中。
注意起点和终点不一定是两端。 所以再在 \(a\) 数组上跑一遍最大子段和板子就好了...
最后的结果把 \(f\) 数组扫一遍取最大值。
写的过程中踩的坑:
- 看错了数据范围
- 理解错了题意
- dp 数组不赋初值
总之一塌糊涂/kk
#include<bits/stdc++.h>
using namespace std;
const int inf=2e9;
int m,n;
int s[105][20005],a[20005];
int f[20005],ans;
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++){a[i]=-inf;f[i]=-inf;ans=-inf;}
for(int i=1;i<=m;i++)
{
for(int j=1;j<n;j++)
{
scanf("%d",&s[i][j]);
a[j]=max(a[j],s[i][j]);
}
}
//for(int i=1;i<n;i++) cout<<a[i]<<" ";
for(int i=1;i<n;i++)
{
f[i]=max(f[i-1]+a[i],a[i]);
ans=max(ans,f[i]);
}
cout<<ans;
return 0;
}
P2095 营养膳食
按脂肪含量排序,然后一个个扫一遍判断是否超出限制,没有的话就吃掉。
#include<bits/stdc++.h>
using namespace std;
int n,m,k,num[110],pan[110]={0},ok=1,ans=0;
struct node{
int a,b;
}s[210];
bool cmp(node x,node y){
return x.a<y.a;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++) scanf("%d",&num[i]);
for(int i=1;i<=n;i++) scanf("%d%d",&s[i].a,&s[i].b);
sort(s+1,s+n+1,cmp);
for(int i=n;i;i--)
{
if(ok>m) break;
if(pan[s[i].b]<num[s[i].b])
{
ans+=s[i].a;
ok++;pan[s[i].b]++;
}
}
cout<<ans<<endl;
return 0;
}
本文来自博客园,作者:樱雪喵,转载请注明原文链接:https://www.cnblogs.com/ying-xue/p/tan-xin-suan-fa.html