p1(貪心)
自己想的糟糕的演算法:
#include//從大到小排序龍頭和騎士,每個龍頭由「恰好」能砍掉的騎士來砍
#include//貌似沒問題,但是又難寫又慢
#include//就當複習stl了
#includeusing namespace std;
int n=1,m=1;
int a[30000];
vectorb;
vector::iterator iter;
bool boo[30000];
int b1;
bool cmp(int x,int y)
int main()
flag=false;
ans=0;
sort(a,a+n,cmp);
sort(b.begin(),b.end());
for(i=0;i
#include//從小到大排序龍頭和騎士,每個騎士砍對應龍頭,如果砍不了就換成下乙個,直到能砍了或騎士用完了為止
#includeusing namespace std;
const int maxn=20005;
int a[maxn],b[maxn];
int main()
if(cur
首先是直覺給出演算法:
猜想1:
執行時間長的先交代。
猜想2:
交代時間長的先交代。
然後是證明與完善:
考慮有相鄰執行的兩個任務x和y,現在要確定它們的順序,顯然它們的順序不會影響其它任務的完成時間。
①當它們執行時間一樣長時,不論交代時間關係如何,顯然順序並不影響完成時間。
由此,猜想2被否決。
並且,對於猜想1得到了完善:執行時間一樣長時先後順序任意。
②當它們執行時間不一樣長時:不妨設交換前先交代x。
a.交換前x比y後結束。
顯然,無論交代時間關係如何,交換它們只能讓完成時間更長。
b.交換前x比y先結束。
顯然,交換前總時間是x交+y交+y執,交換後是y交+max(y執,x交+x執)
那麼何時交換後能使時間變短呢?(如果說交換後時間變短,也就是說交換後的順序更好)
情況1:y執行那麼,交換後是y交+x交+x執
如果使時間變短,則x交+y交+y執》y交+x交+x執,即(x交+y交+y執)-(y交+x交+x執)>0,所以y執》x執
情況2:y執》=x交+x執
那麼,交換前總時間是x交+y交+y執,交換後是y交+y執,顯然時間變短,而此時顯然y執》x執
綜上所述,如果y執行時間更長,就應交換x和y的順序,使y先執行,也就是執行時間長的應該先執行。
(只列出x交代時間較長的情況,x交代時間較短也是類似的)
p4首先,舉個例子,1號給了2號1個金幣,2號給了1號3個,可以簡化為2號給1號2個金幣。
因此,把兩個人間金幣的交換簡化為單方向的給金幣。
xi表示第i個人給了第i-1個人xi個金幣(2<=i<=n) (x1表示第1個人給了第n個人x1個金幣)(如果xi為負則表示第i-1個人給了第i個人-xi個金幣)。
ai表示第i個人原有金幣。m表示每個人最後應有的金幣。
可以列出方程m=a1-x1+x2=a2-x2+x3=a3-x3+x4=...
則對於式1,x2=m-a1+x1
對於式2,m=a2-m+a1-x1+x3,x3=2m-a1-a2+x1=(m-a1)+(m-a2)+x1
對於式3,m=a3-2m+a1+a2-x1+x4,x4=3m-a1-a2-a3+x1=(m-a1)+(m-a2)+(m-a3)+x1
定義ci=(a1+a2+...+ai)-i*m
則x2=x1-c1
x3=x1-c2
x4=x1-c3
xn=x1-c(n-1)
現在,我們要求|x1|+|x2|+...+|xn|的最小值
就是|x1|+|x1-c1|+|x1-c2|+...+|xn-c(n-1)|的最小值
而c1到c(n-1)的值都是已經確定的,因此現在就是在數軸上找乙個點到0,c1,c2,...,c(n-1)的距離之和最小
可以證明,當這個點對應的數是0,c1,c2,...,c(n-1)的中位數時距離之和最小。
怎麼證明
//快速選擇,中位數bfprt
// #include#include#includeusing namespace std;
typedef long long ll;
ll c[1000100];
ll n,sum,m,ans;
//ll abs(ll a)
//int main()
m=c[n]/n;
for(i=1;i<=n;i++)
c[i]-=i*m;
sort(c,c+n);//不要寫成sort(c+1,c+n+1);,這樣會出bug
//最後要求的是|b1|+|b1-c1|+|b1-c2|+...+|b1-c(n-1)|的最小值,不涉及到cn
//可以令c0=0,那麼式子變為|b1-c0|+|b1-c1|+|b1-c2|+...+|b1-c(n-1)|
//這樣就可以變為求c[0]到c[n-1]的中位數
//因此,可以排序c[0]到c[n-1],然後取c[n/2]
b1=c[n/2];//舉例:0,1,2則為1;0,1,2,3則為1或2
// ans=abs(b1);
// for(i=2;i<=n;i++)
// ans+=abs(b1-c[i-1]);//錯在此時陣列c已經排完序,c[0]不再為0,只能按|b1-c0|+|b1-c1|+|b1-c2|+...+|b1-c(n-1)|來求
ans=0;//曾經因為前面的錯誤,忘記加上初始化
for(i=0;i
演算法競賽入門經典 訓練指南 筆記
p1 貪心 自己想的糟糕的演算法 include 從大到小排序龍頭和騎士,每個龍頭由 恰好 能砍掉的騎士來砍 include 貌似沒問題,但是又難寫又慢 include 就當複習stl了 includeusing namespace std int n 1,m 1 int a 30000 vecto...
演算法競賽入門經典訓練指南 4 1學習筆記
1 平面座標系下,向量和點一樣也用x,y表示,等於向量的起點到終點的位移,也相當於把起點平移到座標原點後終點的座標。向量基本運算 struct point typedef point vector 從程式實現上,vector只是point的別名 向量 向量 向量,點 向量 點 vector oper...
演算法競賽入門經典訓練指南 4 1 1學習筆記
點積 兩個向量v和w的點積等於兩者長度的乘積再乘上它們的夾角的余弦。夾角是指v到w的逆時針旋轉的角。夾角大於90度時積為負。255頁2段為止 double dot vector a,vector b double length vector a double angle vector a,vecto...