藍橋杯備賽(六)貪心算法
貪心的意義:(1)最終取得最優值
(2)比較短視,只看眼前的利益
- Acwing 1055. 股票買賣 II
給定一個長度爲 N的數組,數組中的第 i個數字表示一個給定股票在第 i天的價格。
設計一個算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。
注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
輸入格式
第一行包含整數 N,表示數組長度。
第二行包含 N 個不大於 10000的正整數,表示完整的數組。
輸出格式
輸出一個整數,表示最大利潤。
數據範圍
1≤N≤105
輸入樣例1:
6
7 1 5 3 6 4
輸出樣例1:
7
輸入樣例2:
5
1 2 3 4 5
輸出樣例2:
4
輸入樣例3:
5
7 6 4 3 1
輸出樣例3:
0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int n,res;
int a[N];
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=0;i<n-1;i++)
{
int dt=a[i+1]-a[i];
if(dt>0) //貪心算法,只要後一個比前一個的貴,馬上賣掉
res=res+dt;
}
cout<<res;
getchar();getchar();
return 0;
}
- Acwing 104. 貨倉選址
在一條數軸上有 NN 家商店,它們的座標分別爲 A1A1~ANAN。
現在需要在數軸上建立一家貨倉,每天清晨,從貨倉到每家商店都要運送一車商品。
爲了提高效率,求把貨倉建在何處,可以使得貨倉到每家商店的距離之和最小。
輸入格式
第一行輸入整數N。
第二行N個整數A1A1~ANAN。
輸出格式
輸出一個整數,表示距離之和的最小值。
數據範圍
1≤N≤1000001≤N≤100000
輸入樣例:
4
6 2 9 1
輸出樣例:
12
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int n;
long long res;
int a[N];
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
int c=a[n/2]; //猜想。倉庫在中間的點時距離最小
for(int i=0;i<n;i++)
res=res+abs(a[i]-c);
cout<<res;
getchar();getchar();
return 0;
}
- Acwing 122. 糖果傳遞
有n個小朋友坐成一圈,每人有a[i]個糖果。
每人只能給左右兩人傳遞糖果。
每人每次傳遞一個糖果代價爲1。
求使所有人獲得均等糖果的最小代價。
輸入格式
第一行輸入一個正整數n,表示小朋友的個數。
接下來n行,每行一個整數a[i],表示第i個小朋友初始得到的糖果的顆數。
輸出格式
輸出一個整數,表示最小代價。
數據範圍
1≤n≤1000000
數據保證一定有解。
輸入樣例:
4
1
2
5
4
輸出樣例:
4
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000010;
int n;
int a[N];
long long c[N],sum,avg;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) sum+=a[i];
avg=sum/n;
for(int i=n;i>1;i--)
c[i]=c[i+1]+avg-a[i];
c[1]=0;
sort(c+1,c+n+1);
long long res=0;
for(int i=1;i<=n;i++) res+=abs(c[i]-c[(n+1)/2]);
cout<<res;
getchar();getchar();
return 0;
}
- Acwing 112. 雷達設備
假設海岸是一條無限長的直線,陸地位於海岸的一側,海洋位於另外一側。
每個小島都位於海洋一側的某個點上。
雷達裝置均位於海岸線上,且雷達的監測範圍爲d,當小島與某雷達的距離不超過d時,該小島可以被雷達覆蓋。
我們使用笛卡爾座標系,定義海岸線爲x軸,海的一側在x軸上方,陸地一側在x軸下方。
現在給出每個小島的具體座標以及雷達的檢測範圍,請你求出能夠使所有小島都被雷達覆蓋所需的最小雷達數目。
輸入格式
第一行輸入兩個整數n和d,分別代表小島數目和雷達檢測範圍。
接下來n行,每行輸入兩個整數,分別代表小島的x,y軸座標。
同一行數據之間用空格隔開。
輸出格式
輸出一個整數,代表所需的最小雷達數目,若沒有解決方案則所需數目輸出“-1”。
數據範圍
1≤n≤10001≤n≤1000
輸入樣例:
3 2
1 2
-3 1
2 1
輸出樣例:
2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1010;
int n,d;
struct Segment
{
double l,r;
bool operator < (const Segment& t) const
{
return r<t.r;
}
}seg[N];
int main()
{
cin>>n>>d;
bool failed=false;
for(int i=0;i<n;i++)
{
int x,y;
cin>>x>>y;
//將平面問題轉化到橫軸上的一個個區間
if(y>d) failed=true; //如果比半徑大,直接fail
else
{
double len=sqrt(d*d-y*y);
seg[i].l=x-len,seg[i].r=x+len; //區間左端點l,區間右端點r
}
}
if(failed)
puts("-1");
else
{
sort(seg,seg+n);
int cnt=0;
double last=-1e20; //last表示上一個區間的右端點
for(int i=0;i<n;i++)
{
if(last<seg[i].l)
{
cnt++; //若下一個區間左端點比上一個區間的右端點小,那麼就要再新加一個圓
last=seg[i].r; //更新
}
}
cout<<cnt;
}
getchar();getchar();
return 0;
}
- Acwing 1235. 付賬問題
幾個人一起出去喫飯是常有的事。
但在結帳的時候,常常會出現一些爭執。
現在有 n個人出去喫飯,他們總共消費了 S元。
其中第 i 個人帶了 ai元。
幸運的是,所有人帶的錢的總數是足夠付賬的,但現在問題來了:每個人分別要出多少錢呢?
爲了公平起見,我們希望在總付錢量恰好爲 SS 的前提下,最後每個人付的錢的標準差最小。
這裏我們約定,每個人支付的錢數可以是任意非負實數,即可以不是 11 分錢的整數倍。
你需要輸出最小的標準差是多少。
標準差的介紹:標準差是多個數與它們平均數差值的平方平均數,一般用於刻畫這些數之間的“偏差有多大”。
形式化地說,設第 ii 個人付的錢爲 bibi 元,那麼標準差爲 :
輸入格式
第一行包含兩個整數 n、Sn、S;
第二行包含 nn 個非負整數 a1, …, ana1, …, an。
輸出格式
輸出最小的標準差,四捨五入保留 4 位小數。
數據範圍
1≤n≤5×105
0≤ai,S≤109
輸入樣例1:
5 2333
666 666 666 666 666
輸出樣例1:
0.0000
輸入樣例2:
10 30
2 1 4 7 4 8 3 6 4 7
輸出樣例2:
0.7928
首先我們要知道標準差表示的是數據的波動程度,其值越大波動越大。要使得標準差小,我們就要儘可能使得數據都比較接近平均值。那麼這題貪心策略應該是這樣的:首先算出平均值s/n,把數據從小到大排序,如果某個人的錢低於該值,那麼他一定是將錢全部支付,然後其餘不夠的其他人平攤。但是,由於之前那個人錢不夠,那麼就會導致剩下人支付的平均值會增大,所以在這個平攤過程中很有可能存在某個人錢又低於這個平均值,又需要剩下的人平攤。如此反覆,直到支付完成。
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
const int N= 500010;
int n;
double S;
int a[N];
int main()
{
cin>>n>>S;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n); //需要排序,若大的排到前面會浪費值
double avg=S/n; //均值
double lavg =avg; //剩餘部分平均值
double res=S; //剩餘要付的錢
double r=0; //標準差
for(int i=0;i<n;i++)
{
if(a[i]<=lavg) //如果手上的錢比均值小,全部出錢
{
r=r+(avg-a[i])*(avg-a[i]);
res=res-a[i];
lavg=res/(n-i-1); //更新剩餘部分的均值
}
else //若比均值大,只出均值的錢
{
r=r+(avg-lavg)*(avg-lavg);
res=res-lavg;
}
}
printf("%.4lf",sqrt(r/n));
getchar();getchar();
return 0;
}
- Acwing 1239. 乘積最大
給定 N 個整數 A1,A2,…AN。
請你從中選出 K個數,使其乘積最大。
請你求出最大的乘積,由於乘積可能超出整型範圍,你只需輸出乘積除以 1000000009 的餘數。
注意,如果 X<0, 我們定義 X 除以 1000000009的餘數是負(−X)除以 1000000009的餘數,即:0−((0−x)%1000000009)
輸入格式
第一行包含兩個整數 N 和 K。
以下 N行每行一個整數 Ai。
輸出格式
輸出一個整數,表示答案。
數據範圍
1≤K≤N≤105
−105≤Ai≤105
輸入樣例1:
5 3
-100000
-10000
2
100000
10000
輸出樣例1:
999100009
輸入樣例2:
5 3
-100000
-100000
-2
-100000
-100000
輸出樣例2:
-999999829
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=100010,mod = 1000000009 ;
int a[N];
int main()
{
int k,n;
cin>>n>>k;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
long long res=1; //乘積
int l=0,r=n-1; //雙指針
int sign=1; //符號
if(k%2) //若k爲奇數,則需要先選出最大的正數
{
res=a[r];
r--;
k--;
if(res<0) sign=-1; // 如果最大值都是負數,就證明全是負數,那麼符號要發生改變
}
while(k)
{
long long x=(long long)a[l]*a[l+1],y=(long long)a[r]*a[r-1];
//從左右兩端一對一對選擇較大的
if(x*sign>y*sign)
{
res=x%mod*res%mod;
l+=2; //指針移動
}
else
{
res=y%mod*res%mod;
r-=2;
}
k-=2;
}
cout<<res;
getchar();getchar();
return 0;
}
- Acwing 1247. 後綴表達式
給定 N 個加號、M個減號以及 N+M+1 個整數 A1,A2,⋅⋅⋅,AN+M+1,小明想知道在所有由這 N 個加號、M個減號以及 N+M+1個整數湊出的合法的後綴表達式中,結果最大的是哪一個?
請你輸出這個最大的結果。
例如使用 123+−,則 “23+1−”這個後綴表達式結果是 4,是最大的。
輸入格式
第一行包含兩個整數 N 和 M。
第二行包含 N+M+1個整數 A1,A2,⋅⋅⋅,AN+M+1。
輸出格式
輸出一個整數,代表答案。
數據範圍
0≤N,M≤105
−109≤Ai≤109
輸入樣例:
1 1
1 2 3
輸出樣例:
4
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=200010;
int a[N],m,n;
int main()
{
long long res=0;
cin>>n>>m;
for(int i=0;i<m+n+1;i++) scanf("%d",&a[i]);
sort(a,a+m+n+1);
if(!m) //沒有負號,直接全加
{
for(int i=0;i<m+n+1;i++)
res=res+a[i];
}
else //有負號
{
res=a[m+n]-a[0]; //一定有一個正數,一個負數,所以用最大值減最小值
for(int i=1;i<m+n;i++)
res=res+abs(a[i]); //其他符號任選,全部選爲正
}
cout<<res;
getchar();getchar();
return 0;
}
第一部分完結撒花啦!!!
之後開始對前面所學的和題進行消化與刷題!!!