題目傳送門
題意解析:題目就是告訴我們n塊木板,每塊木板都有一個長度。然後一開始有一塊木板,這塊木板的長度是n塊木板之和,然後每次都要把一塊木板鋸開,每次的費用就是木板的長度,最後問題得到一開始n塊木板的費用最小值。
My opinion:一開始看到這題時一臉懵逼的,完全不會,怎麼鋸?難道dfs,因爲你不知道這塊木板該鋸成什麼大小。又想了一會,突然發現這題跟合併果子差不多,只不過這題時倒着來的,我們可以合併這些木板,跟鋸開這些木板的費用是一樣的,這樣就好辦了,我們每次找出最小的兩個值合併就ok了。
總結:
1、輸入,建堆。
2、每次找出兩個合併,並加在答案上。
3、輸出。
用了左偏樹的代碼:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define Clear(a,x) memset(a,x,sizeof(a))
#define ll long long
#define INF 2000000000
#define eps 1e-8
using namespace std;
ll read(){
ll x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int maxn=20005;
struct node{
int l,r,dis;
ll key;
}a[maxn<<1];
int n,now;
ll ans;
int merge(int A,int B){
if (A==0) return B;
if (B==0) return A;
if (a[A].key>a[B].key)
swap(A,B);
a[A].r=merge(a[A].r,B);
if (a[a[A].r].dis>a[a[A].l].dis)
swap(a[A].l,a[A].r);
a[A].dis=a[a[A].r].dis+1;
return A;
}
int top(){
int l=a[now].l,r=a[now].r;
a[now].l=0,a[now].r=0;
ll sum=a[now].key;
now=merge(l,r);
return sum;
}
int main(){
n=read();
now=1;ans=0;
rep(i,1,n){
a[i].key=read();
a[i].l=a[i].r=0;
a[i].dis=0;
if (i!=1) now=merge(now,i);
}
int len=n;
rep(i,1,len-1){
int x=top(),y=top();
ans+=x+y;
a[++n].key=x+y;
a[n].l=0,a[n].r=0;
a[n].dis=0;
now=merge(now,n);
}
printf("%lld\n",ans);
return 0;
}
因爲dalao把這題放在了左偏樹專題上,所以我用了左偏樹,事實上兩個用兩個隊列也可以解決(可能吧,反正合並果子可以)。