NOI2007貨幣兌換CASH 斜率DP

2021-07-16 00:09:19 字數 3080 閱讀 3412

做到一道題,不會做。

題解說是fft+ cdq分治。

學會了fft,發現不會cdq分治。

去看cdq的**,發現用了cash這道題……

然後貌似有斜率dp的東西,就順便學了斜率dp,寫了之前hdu的一道題,繼續寫這道題……

各種題解各種splay,我一想,這道題用map就行了啊……然後寫了200多行,還超級慢……

最囧的問題,在我和朋友的電腦上,用noi的資料ac,在vijos上,wa6,後面t了。

後來發現因為我偷懶,反覆呼叫map超級慢,但是不想改了。。就這樣吧,算是理解斜率dp的各種囧東西了。 以後再有空補splay寫法吧(自我安慰,不會補了……)

至於題解,網上到處都有……我再重複一次吧,如果有人需要的話。

f[i]表示第i天,所能獲得的最大 金錢數量。

如果知道了f[i],那麼我們可以用x[i]表示第i天的錢,全部買a卷,能購買的數量,y[i]則為b卷數量。

至於x[i], y[i]的求解,就不再贅述……(列方程表示一下就能求出來啦~)

然後f[i]可以寫成類似於 f[i] = max}的形式

不考慮獅子中f[i-1] 的部分,強行變換一下

f[i]=x[j]*a[i]+y[j]*b[i]

式子變形後為

f[i]/b[i] - x[j]*(a[i]/b[i])=y[j]

a[i],b[i]為常數,也就是第i天a,b卷的**。

這個式子形如y=kx+b

求i的時候,[1,i-1]的f值都已經知道了,所以可以弄出很多x[j],y[j]的點對。 相當於我在一張圖上,有很多(x,y)的座標,我有乙個斜率k,要找乙個點,去放上這個斜率k,使得方程中的b最大。 

y=kx+b是截距式,並且k恒為負數(a,b為正數,式子中有符號。  同時,題目給定的rate比例是[0,100],保證了b為非0,所以斜率永遠存在~)

現在問題就轉變為,根據前面的亂七八糟的x,y的點座標,找乙個最優的點座標,來匹配上k,求出f[i]的值啦

然後顯然(這個顯然可以自己畫圖……當然別人的文章有圖啦~我比較懶) 解一定在上凸殼上,所以首先我們要維護乙個上凸殼。

對於每次插入新的點,可以看這個點在凸殼內部(下方),還是上方。因為凸殼的點 x座標是有序的,所以可以用map維護x座標,和x座標裡儲存的y座標資訊,以及這個點和左邊的點的斜率,和右邊點的斜率。

比如說,第三個點,和第二個點斜率, 以及和第四個點的斜率。維護這些資訊會方便一些~

【第乙個點和左邊的點斜率為0, 最後乙個點和右邊的點斜率為負無窮】 這個很重要,問我為啥知道的。。我舉例子弄的……沒法證明。

然後再用乙個map來維護斜率,對映斜率對應的x座標即可。

然後維護這兩個map,我的**太臃腫,並且因為偷懶,反覆呼叫了很多東西。而且確實,我的map水平不堪入目啊……

然後細節很多。。。

情況1: 插入的點,其x座標是已插入點最大/最小的。

情況2:插入的點,和之前插入的點,x座標相同(若新點y座標更小,直接拋棄,否則需要刪除曾經插入的點,來插入新的點)

還有一些情況,在寫map的時候回自己感覺到。。。如果寫 splay,就不會因為出現不好判斷(--map.begin())之類的情況的問題啦~ 其實寫其他平衡樹也行。

上**:很慢,最慢的3秒多。

#include #include #include #include #include #include #include using namespace std;

typedef long long ll;

int n, s;

const double inf = -1000000000000;

const int maxn = 100000 +100;

double a[maxn], b[maxn], r[maxn];

double f[maxn];

struct node

node(){}

};typedef map::iterator mit;

mapmp; //double表示那個點座標再x位置,node是那個點的各種資訊

mapxie; //表示first為斜率, second是x座標是x'的,和比x小的那個座標的斜率。

double xielv(node a, node b)

double getx(int k)//已知道f[k]的情況下, 求出x[k]

double gety(int k)

void init()

double get_f(int p)//條件充分,計算f[p]值

void del(double x)

void ins(double x, double y)

if (l_it == mp.begin() && xielv(it->second, l_it -> second) >= (l_it -> second).lk)

dont_delte_left:;

if (it == mp.begin()) //自己已經是最左邊界的情況

else

while (r_it != mp.end() && xielv(it-> second, r_it-> second) <= (r_it -> second).rk)

if (it == --mp.end())

else

xie[it->second.lk] = it-> second;

}void doit()

mp[100000000] = node(0, 100000000,0,0);

mp[inf] = node(100000000,0,0,0);

it = mp.upper_bound(x);

double kr = xielv(it -> second, node(x,y,0,0));

double kl = xielv((--it) -> second, node(x, y, 0, 0));

mp.erase(100000000);

mp.erase(inf);

if (kl < kr) //在凸殼內

ins(x, y);

} printf("%.3f\n", f[n]);

}int main()

return 0;

}

NOI2007 貨幣兌換

今天聽了crazy和samjia的noi雜 砸 題選講,感覺自己萌萌噠 於是就來怡情地寫了這道題。額 o 這個不好說啊。語文不好不好裱我 還是貼圖吧。咳咳,希望大家都看懂題了。乙個很明顯的貪心思路就是,我們每天要不全買,要不全賣。因為一有利益我們就去佔,一有虧損我們就不碰。那麼我們可以有dp方程 f...

Noi2007 貨幣兌換

傳送門 小半個上午 一下午都給了這題了qaq 都知道是斜率優化,問題是我看這題根本就是乙個人乙個式子啊 算了,把我的過程列出來吧 令 f i 表示第i天結束時強制賣出所有金券最多能得到的軟妹幣數量,列舉上一次 金券的時間,就有 beginf i max end 這裡沒有寫隱含條件 f i ge f ...

NOI2007 貨幣兌換

題目 先來畫一畫柿子 設 dp i 表示你第 i 天之後最多剩下多少錢 考慮一下對於 i 的轉移,我們肯定要在之前列舉一天 j 這一天把所有的東西買進來,之後在 i 天賣掉 設那天買進 a 的量為 d a 買進 b 的量為 d b 我們可以得到這樣的方程 d ap a d bp b dp j d a...