1. 程式人生 > >「一本通」斜率優化dp學習筆記

「一本通」斜率優化dp學習筆記

總結:

如果dp方程寫出來之後大概是長這樣的f[i]=0&lt;j&lt;imin(f[j]+s[i,j])+f[i]=\sum_{0&lt;j&lt;i} min(f[j]+s[i,j])+…,就可以考慮斜率優化(關於斜率: y[i]y[j]x[i]x[j]\frac{y[i]-y[j]}{x[i]-x[j]})。先設k1k_1<k2k2,分析一下是否有f[k1]f[k_1]<f[k2]f[k2](證明決策單調性),然後大力推一發柿子(通常是把關於j

1j1j2j2的項移到座標,關於i的項移到右邊),找到xx座標和yy座標所對應的值,用單調佇列維護有用的決策,就可以亂搞ac了

loj#10184. 「一本通 5.6 例 1」任務安排 1

這題應該是先教你搞出dp公式 注意題意,執行任務的時間是同一批任務的所用時間總和+啟動時間,費用算的是每一個任務所屬批次的結束時間*費用係數 設t[i]t[i]為時間的字首和,c[i]c[i]為費用的字首和,$f[i]f[i]為執行1~i 的最少花費,列舉當前批次的任務為 j ~ i,顯然有當前當前批次的費用(t[i]+s)(c[i]c[j])(t[i]+s)*(c[i]-c[j])

,機器的啟動時間S會對後面的所有任務的完成時間都產生一個大小為S的後延,補充到當前的費用中,此時f[i]=1&lt;=j&lt;imin(f[j]+(t[i]+S)(c[i]c[j])+(c[n]c[i])S)f[i]=\sum_{1&lt;=j&lt;i} min(f[j]+(t[i]+S)*(c[i]-c[j])+(c[n]-c[i])*S),這題的資料範圍1N50001≤N≤5000,可以放心N2N^2 dp

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int list[5100];
long long f[5100],t[5100],c[5100];
int main()
{
	int n,S;
	scanf("%d%d",&n,&S);
	for (int i=1;i<=n;i++)
	{
		long long tt,cc;
		scanf("%lld%lld",&tt,&cc);
		t[i]=t[i-1]+tt;
		c[i]=c[i-1]+cc;
	}
	memset(f,63,sizeof(f));
	f[0]=0;
	for (int i=1;i<=n;i++)
	{
		for (int j=0;j<i;j++)
		{
			f[i]=min(f[j]+(t[i]+S)*(c[i]-c[j])+(c[n]-c[i])*S,f[i]);
		}
	}
	printf("%lld\n",f[n]);
	return 0;
}

loj#10185. 「一本通 5.6 例 2」任務安排 2

(事實證明用t1的N2N^2做法是能過的)j1&lt;j2j1&lt;j2,試證明j2j2的決策比j1j1優秀 不想證,還是去看進階指南吧 f[j1]+(t[i]+S)(c[i]c[j1])+(c[n]c[i])S&lt;f[j1]+(t[i]+S)(c[i]c[j1])+(c[n]c[i])Sf[j1]+(t[i]+S)*(c[i]-c[j1])+(c[n]-c[i])*S&lt;f[j1]+(t[i]+S)*(c[i]-c[j1])+(c[n]-c[i])*S 拆括號,抵消,移項,得 f[j1]Sc[j1]f[j2]+Sc[j2]&lt;t[i]c[j2]t[i]c[j1]f[j1]-S*c[j1]-f[j2]+S*c[j2]&lt;t[i]*c[j2]-t[i]*c[j1] 右邊只保留t[i]的項, (f[j1]Sc[j1])(f[j2]Sc[j2])c[j1]+c[j2]&lt;t[i]\frac{(f[j1]-S*c[j1])-(f[j2]-S*c[j2])}{-c[j1]+c[j2]}&lt;t[i] 得到公式啦! 根據“及時排出無用決策”,本題應是要維護一個下凸殼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int list[11000];
long long f[11000],t[11000],c[11000];
int n,S;
int X(int i)
{
	return c[i];
}
int Y(int i)
{
	return f[i]-S*c[i];
}
double slop(int j1,int j2)
{
	return double(Y(j2)-Y(j1))/double(X(j2)-X(j1));
}
int main()
{
	scanf("%d%d",&n,&S);
	for (int i=1;i<=n;i++)
	{
		long long tt,cc;
		scanf("%lld%lld",&tt,&cc);
		t[i]=t[i-1]+tt;
		c[i]=c[i-1]+cc;
	}
	int head=1,tail=1;
	for (int i=1;i<=n;i++)
	{
		while (head+1<=tail&&slop(list[head],list[head+1])<t[i]) head++;
		f[i]=f[list[head]]+(t[i]+S)*(c[i]-c[list[head]])+(c[n]-c[i])*S;
		while (head<=tail-1&&slop(list[tail],i)<slop(list[tail-1],list[tail])) tail--;
		list[++tail]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}

loj#10186. 「一本通 5.6 例 3」任務安排 3

https://loj.ac/problem/10186 時間可能為負數,因此時間的字首和t[i]也不具有單調性 而我們要維護斜率還是具有單調性的!! 結束刪隊頭的操作,最後出來的list[head]左邊的斜率小於t[i],右邊的斜率大於t[i] 所以通過 二分查詢到 左邊的斜率小於t[i],右邊的斜率大於t[i]的點 !!sd模版騙我錢財!! 數字太大還是要把除移項變成乘好一點

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int list[310000];
LL f[310000],t[310000],c[310000];
int n,S;
LL X(int i)
{
	return c[i];
}
LL Y(int i)
{
	return f[i]-S*c[i];
}
double slop(int j1,int j2)
{
	return double(Y(j2)-Y(j1))/double(X(j2)-X(j1));
}
int main()
{
	scanf("%d%d",&n,&S);
	for (int i=1;i<=n;i++)
	{
		int tt,cc;
		scanf("%d%d",&tt,&cc);
		t[i]=t[i-1]+tt;
		c[i]=c[i-1]+cc;
	}
	int head=1,tail=1; list[1]=0;
	for (int i=1;i<=n;i++)
	{
		//while (head+1<=tail&&slop(list[head],list[head+1])<t[i]) head++;
		int l=head,r=tail;
		while (l<r)
		{
			int mid=(l+r)/2;
			if (f[list[mid]]-f[list[mid+1]]>=(S+t[i])*(c[list[mid]]-c[list[mid+1]])) l=mid+1; else r=mid;
		} 
		f[i]=f[list[l]]-(S+t[i])*c[list[l]]+t[i]*c[i]+S*c[n];
		while (head<=tail-1&&(f[list[tail]]-f[list[tail-1]])*(c[i]-c[list[tail]])>=(f[i]-f[list[tail]])*(c[list[tail]]-c[list[tail-1]])) tail--;
		list[++tail]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}

loj#10187. 「一本通 5.6 例 4」Cats Transport

https://loj.ac/problem/10187 d[i]d[i]為d的字首和,即1號山到i號山的距離 如果想要接到第i只貓,就得在t[i]d[h[i]]t[i]-d[h[i]]時或之後出發,才能在貓玩夠之後到達 令a[i]=t[i]d[h[i]]a[i]=t[i]-d[h[i]],對a排序,根據貪心策略,一個飼養員接到的貓一定是a[]排序後連續的一段,那麼此時問題又回到了任務安排2了

s[i]s[i]為a[ ]的字首和,f[i][j]為前i個飼養員,接到前j只貓的最小等待時間 有dp方程f[i][j]=min(f[i1][k](s[j]s[k])+a[j](jk))f[i][j]=min(f[i-1][k]-(s[j]-s[k])+a[j]*(j-k))

k1&lt;k2k1&lt;k2,因為時間,路程均為正整數,一定有k2優於k1 f[i1][k1](s[j]s[k1])+a[j](jk1)&gt;f[i1][k2](s[j]s[k2])+a[j](jk2)f[i-1][k1]-(s[j]-s[k1])+a[j]*(j-k1)&gt;f[i-1][k2]-(s[j]-s[k2])+a[j]*(j-k2) 拆括號,抵消,移項,注意k1k2&lt;0k1-k2&lt;0,得 (f[i1][k1]s[k1])(a[i1][k2]s[k2])k1k2&lt;a[j]\frac{(f[i-1][k1]-s[k1])-(a[i-1][k2]-s[k2])}{k1-k2}&lt;a[j]

上一題血的教訓告訴我寫程式碼還是把它弄成乘法吧

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
LL d[110000],a[110000],s[110000],list[110000];
LL f[110][110000];//f[飼養員i][收回前j只貓]
bool cmp(LL x,LL y) {return x<y;}
LL Y(LL i,int k)
{
	return f[i][k]+s[k];
}
LL X(LL k)
{
	return k;
}
int main()
{
	int n,m,p;
	scanf("%d%d%d",&n,&m,&p);
	for (int i=2;i<=n;i++) {scanf("%d",&d[i]);d[i]+=d[i-1];}
	for (int i=1;i<=m;i++)
	{
		int h,t;
		scanf("%d%d",&h,&t);
		a[i]=t-d[h];
	}
	sort(a+1,a+1+m,cmp);
	for (int i=1;i<=m;i++) s[i]=s[i-1]+a[i];
	memset(f,63,sizeof(f)); 
	for (int i=0;i<=p;i++) f[i][0]=0;
	for (int i=1;i<=p;i++)
	{
		int head=1,tail=1; list[1]=0;
		for (int j=1;j<=m;j++)
		{
			while (head+1<=tail&& (Y(i-1,list[head])-Y(i-1,list[head+1]))>=a[j]*(list[head]-list[head+1])) head++;
			f[i][j]=min(f[i-1][list[head]]+a[j]*(j-list[head])-(s[j]-s[list[head]]),f[i-1][j]);
			if (f[i-1][j]+s[j]>=455743088879883099) continue;
			while (head<=tail-1&& (Y(i-1,list[tail-1]) - Y(i-1,list[tail])) * (list[tail]-j) > (Y(i-1,list[tail])-Y(i-1,j))*(list[tail-1]-list[tail])) tail--;
			list[++tail]=j;
		}
	}
	printf("%lld\n",f[p][m]);
	return 0;
}

loj#10188. 「一本通 5.6 練習 1」玩具裝箱

https://loj.ac/problem/10188 sum[i]為玩具長度的字首和 顯然有f[i]=1j&lt;imin(f[j]+(sum[i]sum[j]+ij1L)2)f[i]=\sum_{1≤j&lt;i}{min(f[j]+(sum[i]-sum[j]+i-j-1-L)^2)} 令c[i]=sum[i]+i,L=1+L dp方程:f[i]=min(f[j]+(c[i]c[j]L)2)f[i]=min(f[j]+(c[i]-c[j]-L)^2)k1&lt;k2k1&lt;k2 (我喜歡用k) f[k1]+(c[i]c[k1]L)2&lt;f[k2]+(c[i]c[k2]L)2f[k1]+(c[i]-c[k1]-L)^2&lt;f[k2]+(c[i]-c[k2]-L)^2(c[i]L)(c[i]-L)看作一個整體,完全平方公式來一發 f[k1]+(c[i]L)22c[k1](c[i]L)+c[k1]2&lt;f[k2]+(c[i]L)2+2(c[i]L)c[k2]+c[k2]2f[k1]+(c[i]-L)^2-2*c[k1]*(c[i]-L)+c[k1]^2&lt;f[k2]+(c[i]-L)^2+2*(c[i]-L)*c[k2]+c[k2]^2 繼續拆括號,移項,得 f[k1]+c[k1]2(f[k2]+c[k2]2)c[k1]c[k2]&lt;2(c[i]L)\frac{f[k1]+c[k1]^2-(f[k2]+c[k2]^2)}{c[k1]-c[k2]}&lt;2*(c[i]-L)

相關推薦

斜率優化dp學習筆記

總結: 如果dp方程寫出來之後大概是長這樣的f[i]=∑0&lt;j&lt;imin(f[j]+s[i,j])+…f[i]=\sum_{0&lt;j&lt;i} min(

斜率優化DP學習筆記

最近瞎搞了搞斜率優化... 斜率優化DP是一種優化DP的思想,通過高中數學線性規劃那套理論加上凸包那套理論單調的優良性質將O(N^2)的DP列舉轉移優化成O(N)的轉移 先來一道簡單題:[APIO2010]特別行動隊 我們設\(s_i\)是字首和陣列,設\(f_i\)為前\(i\)個巨佬的戰鬥力和最大值

SPFA算法的SLF優化 ——loj#10081. 3.2 練習 7道路和航線

。。 loj dijkstra 分享 spa 思想 text 超時 我見 今天做到一道最短路的題,原題https://loj.ac/problem/10081 題目大意為給一張有n個頂點的圖,點與點之間有m1條道路,m2條航線,道路是雙向的,且權值非負,而航線是單向的,權值

LibreOJ10077. 3.2 練習 3最短路計數【最短路+DP

10077. 「一本通 3.2 練習 3」最短路計數 【題目描述】 傳送門 【題解】 這題我們知道如何判斷這條邊是不是最短路上的邊,那麼就可以DP求解了。但是要注意順序,我們可以預處理出最短路路徑(x,y),然後BFS走DP就可以了。 程式碼如下 #includ

LOJ#10001. 1.1 例 2種樹

記錄 positive UNC minus scu tps ace tin 行為 #10001. 「一本通 1.1 例 2」種樹 內存限制:512 MiB 時間限制:1000 ms 標準輸入輸出 題目類型:傳統 評測方式:文本比較 上傳者: 1benton

LOJ#10002. 1.1 例 3噴水裝置

傳統 lock ted sdl pro prim res baseline bottom #10002. 「一本通 1.1 例 3」噴水裝置 內存限制:512 MiB 時間限制:1000 ms 標準輸入輸出 題目類型:傳統 評測方式:文本比較 上傳者: 1

LOJ#10003. 1.1 例 4加工生產調度

for 時間限制 mar segment html tps text bsp 輸入格式 內存限制:512 MiB 時間限制:1000 ms 標準輸入輸出 題目類型:傳統 評測方式:Special Judge 上傳者: 1bentong 提交 提交記錄

LOJ#10007. 1.1 練習 3線段

ios esp math lin truct algorithm ade html 最大值 題目描述 數軸上有 nnn 條線段,選取其中 kkk 條線段使得這 kkk 條線段兩兩沒有重合部分,問 kkk 最大為多少。 輸入格式

#10023. 1.3 練習 2平板塗色

int 1.3 col ans nbsp can include class efi #include<bits/stdc++.h> #define lop(x,m,n) for(int x=m;x<=n;x++) using namespace std

LOJ #10084. 3.3 練習 1最小圈(二分+SPFA判負環)

ont 題意 二分 size 描述 負環 -s bsp lan 題意描述:    見原LOJ:https://loj.ac/problem/10084 題解:   LOJ #10084. 「一本通 3.3 練習 1」最小圈(二分+SPFA判負環)

LOJ#10051 2.3 例 3Nikitosh 和異或

sizeof nts class challenge inf 技術分享 big ron urn 題目描述 原題來自:CODECHEF September Challenge 2015 REBXOR 1??≤r?1??<l?2??≤r?2

LOJ#10068 3.1 練習 3秘密的牛奶運輸(次小生成樹

source log ems ado john line flex iostream 編號 題目描述 Farmer John 要把他的牛奶運輸到各個銷售點。運輸過程中,可以先把牛奶運輸到一些銷售點,再由這些銷售點分別運輸到其他銷售點。 運輸的總距離越小,運

5.1 練習 2分離與合體 題解

-s color tps oid loj i++ code algo 練習 題目鏈接:這道題... 成功被卡題面。 真的卡題面.... 我用了兩種方法(區間DP和記憶化搜索),這裏直接貼代碼了。 區間DP: #include<iostream>

#10172. 5.4 練習 1塗抹果醬 題解

tst num gist 輸出 def head printf admin line 題目鏈接 一道三進制狀壓的好題。 題目描述: Tyvj 兩周年慶典要到了,Sam 想為 Tyvj 做一個大蛋糕。蛋糕俯視圖是一個 N×M的矩形,它被劃分成

[題解] 1.3 練習 1埃及分數

埃及分數題目連結 這道題比較經典。 演算法:迭代加深+IDA* 優化: 1.迭代加深 2.確定從小到大的搜尋順序 3.確定搜尋上下界 (1)以i為分母的數字不能大於a/b. (2)如果後面的數字都以i為分母仍然<=a/b,退出。 細節: (1)在通分過程中會爆int。 程式碼:

LOJ10131. 4.4 例 2暗的連鎖【樹上差分】

LINK solution 很簡單的題 你就考慮實際上是對每一個邊求出兩端節點分別在兩個子樹裡面的附加邊的數量 然後這個值是0第二次隨便切有m種方案,如果這個值是1第二次只有一種方案 如果這個值是2或者更大沒有方案 然後就可以直接統計答案了 那麼就對每一次查詢的邊 在兩個節點++,lca處

LOJ#10056 2.3 練習 5The XOR-longest Path (Trie LOJ#10050 2.3 例 2The XOR Largest Pair (Trie

#10056. 「一本通 2.3 練習 5」The XOR-longest Path 題目描述 原題來自:POJ 3764 給定一棵 nnn 個點的帶權樹,求樹上最長的異或和路徑。 輸入格式

LOJ#10064. 3.1 例 1黑暗城堡

LOJ#10064. 「一本通 3.1 例 1」黑暗城堡 題目描述 你知道黑暗城堡有$N$個房間,$M$條可以製造的雙向通道,以及每條通道的長度。 城堡是樹形的並且滿足下面的條件: 設$D_i$為如果所有的通道都被修建,第$i$號房間與第$1$號房間的最短路徑長度; 而$S_

LOJ#10051 2.3 例 3Nikitosh 和異或 LOJ#10050 2.3 例 2The XOR Largest Pair(Trie

題目描述 原題來自:CODECHEF September Challenge 2015 REBXOR 1​​≤r​1​​<l​2​​≤r​2​​≤N,x⨁yx\bigoplus yx⨁y 表示 xxx 和 yyy 的按位異或。

LOJ #10222. 6.5 例 4佳佳的 Fibonacci

題目連結 題目大意 $$F[i]=F[i-1]+F[i-2]\ (\ F[1]=1\ ,\ F[2]=1\ )$$ $$T[i]=F[1]+2F[2]+3F[3]+...+nF[n]$$ 求$T[n]\ mod\ m$ $n,m<=2^{31}-1$   這題的遞推式推導有