1. 程式人生 > >【USACO題庫】 動態規劃 彙總

【USACO題庫】 動態規劃 彙總

資料結構能打到省選了=-= 但是其他類問題只能打到普及

普及啊啊啊!!!

而且這邊省選組都是什麼仙人掌啊,什麼系什麼點對啊...感覺資料結構並沒有什麼用 (實際上很有用但我不會運用)

然後頹到提高組來了..結果全是模擬還有一堆沒學的其他玩意 (迴文自動機) 資料結構也沒考幾次..還是線段樹用來輔助搜尋......

看看普及 全是水題 當然偶爾還有一點我不會的 (某些演算法) =-= 萬念俱灰 無奈之下待在了提高組...

於是在老師的強迫我堅毅的信念下開始填坑=-=這裡是dp專題

 找找常見dp的型別......普通dp 樹形dp 區間dp 狀壓dp 數位dp 插頭dp......一個一個補

廢話不多說先放題目

---------------------------------------第二部分---------------------------------------

2.2.2 Subset Sums集合

題目描述

對於從1到N(1 <= n <= 39)的連續整數集合,能劃分成兩個子集合,且保證每個集合的數字和是相等的。

舉個栗子,如果N=3,對於{1,2,3}能劃分成兩個子集合,他們每個的所有數字和是相等的:

  • {3} and {1,2}

這是唯一一種分法(交換集合位置被認為是同一種劃分方案,因此不會增加劃分方案總數)

如果N=7,有四種方法能劃分集合{1,2,3,4,5,6,7},每一種分發的子集合各數字和是相等的:

  • {1,6,7} and {2,3,4,5} {注 1+6+7=2+3+4+5}
  • {2,5,7} and {1,3,4,6}
  • {3,4,7} and {1,2,5,6}
  • {1,2,4,7} and {3,5,6}

給出N,你的程式應該輸出劃分方案總數,如果不存在這樣的劃分方案,則輸出0。程式不能預存結果直接輸出。

時空範圍呢 是1000ms/256MB

INPUT FORMAT

輸入檔案只有一行,且只有一個整數N

SAMPLE INPUT

7

OUTPUT FORMAT

輸出劃分方案總數,如果不存在則輸出0。

SAMPLE OUTPUT

4

先來思考一下=-= n範圍是1~39 那麼死磕演算法肯定是不行的 O(2的n次方)

然後仔細分析題目 每個數字必定屬於一個集合 要不是這個要不是那個 並且兩集合相等 其總和為(n + 1) * n (好像是高斯那啥)

除2即得單個集合的值 然後這集合裡面的數是1~n 可選可不選

不就是01揹包麼=w=

那麼對於每一個數 我們可選 也可不選

設dp[a][b]為 取前a個數 總和為b的方案數量 ...等等 為什麼這麼設??

動態規劃 我們從小找起 一開始只有一個數 可能的值為 1 或 0

然後第二個數 在這兩個值上相加...以此類推

那麼a就是一個一個數到n 然後b呢 因為按上面那種推法可能結果很多 但顯而易見可能結果絕對小於數的總和

因此以數的總和作為b...好了 初始化第一個數 然後開始推

之後呢 看程式碼註釋吧~

下放程式碼:

#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
using namespace std;
ll dp[5 << 4][5 << 7];//坑啊 上限大於2147483647 然後我AC率掉了一截
int main()
{
	int n;
	scanf("%d",&n);//認真仔細地輸入
	int all = (n + n * n) >> 1;//這裡原本是(n + 1) * n / 2 但為了我高大上的位運算=-= 式子怎麼來的不說了 別人高斯小學就發現了
	if (all % 2) {putchar('0'),putchar('\n'); return 0;}//判斷總數如果為單數 絕對不可能平分成兩整數 直接退出 皆大歡喜
	all = all >> 1;//先除以2 因為我發現總數後面用不到
	dp[1][1] = 1;//預處理1個數的情況
	dp[1][0] = 1;//同上
		for (int a = 2 ; a <= n ; a ++)//判斷加上第a個數的情況
			for (int b = 0 ; b <= all ; b ++)//判斷原有所有狀態 更新加上/不加上a的狀態
			if (b < a) dp[a][b] = dp[a - 1][b];//更新不加a達到的數的和的方案數
				  else dp[a][b] = dp[a - 1][b] + dp[a - 1][b - a];//更新加上a達到的數的和的方案數
	printf("%d\n",dp[n][all] / 2);//因為這裡兩個子集一併求了因此要除2... 
	return 0;
}

2.3.2 Cow Pedigrees奶牛家譜

題目描述

農民約翰準備購買一群新奶牛。 在這個新的奶牛群中, 每一個母親奶牛都生兩小奶牛。這些奶牛間的關係可以用二叉樹來表示。這些二叉樹總共有N個節點(3 <= N < 200)。這些二叉樹有如下性質:

  1. 每一個節點的度是0或2。度是這個節點的孩子的數目。
     
  2. 樹的高度等於K(1 < K < 100)。高度是從根到任何葉子的最長的路徑上的節點的數目; 葉子是指沒有孩子的節點。

有多少不同的家譜結構? 如果一個家譜的樹結構不同於另一個的, 那麼這兩個家譜就是不同的。輸出可能的家譜樹的個數除以9901的餘數。

INPUT FORMAT

第1行: 兩個空格分開的整數, N和K。

SAMPLE INPUT

5 3

OUTPUT FORMAT

第 1 行: 一個整數,表示可能的家譜樹的個數除以9901的餘數。

SAMPLE OUTPUT

2

OUTPUT DETAILS:

有5個節點,高為3的兩個不同的家譜:

      @                  @
 
      / \                   / \
   @  @    和    @   @
   / \                         / \
@ @                    @ @

然後別的部落格上有個人解釋和USACO的官方解釋 這裡給個傳送門 Tip:還是回來吧 我這裡詳細得多OvO

分析一下嘛 這題呢

就是求 一定高度 樹上有一定節點 分佈在樹上的方法

然後方法有兩種: 子樹沒節點 或 子樹兩個節點 然後後者子樹下一層的節點也同這兩種

設子樹為2個 是 X 為0個 是 O 則

該層某地方的上層 可能是XOOXXOXXXOXOOXX.........嘛 太複雜了 我們排除掉純暴力的想法 然後......DP!

樹形DP!!!

從根節點開始推 首先我們知道 根節點只有一種情況——有 因此他肯定有兩兒子

嘛 後面的點嘛 就有兩種情況 有或沒有 後面每層都這樣

然後如果要到下一層 那下一層的點肯定有父親 但他父親那層的點有多少...我們只知道這種情況下 只有他父親必然存在

葉子節點必然有父親

那點就是不確定的了......那我們就要推啊 好 我們設點~ (喂喂怎麼能這麼爽快我還不清楚情況呢)

題目正好又給了節點個數 3≤N<200 那我們從3開始推 根據節點度數很輕鬆地能發現 總共點數只可能是奇數 剛好 1 + 2 = 3

那麼從3推起剛好能銜接上!!

Tip:如果想讀入N時預處理去掉偶數情況也可以 但是我懶...反正後面推導後這些情況的答案也是0

嘛 我們點數22累加 算到N就好

嗯 點可以推到N 那深度呢......好 我們設深度~ (喂喂怎麼又是這樣子解釋清楚啊)

吶 我們要求深度為K 點數為N的情況嘛 然後一開始的情況已經知道了 深度為1 點數為1 情況為1

那就推深度和情況啊 (搞了這麼久原來是這個意思...)

嘛 我們深度11累加 算到K就好

這裡考慮點數一定時 分佈在所有深度的情況

因此這裡存的狀態是當前節點數 當前深度及以上(淺) 的 所有狀態

點數一定 點數分佈太多太雜 像上面的XOOXXOXXXOXOOXX.........我們肯定不能對當前節點所在的狀態進行推導 動態規劃嘛

說了點數一定 那就按當前點數推 點數分佈...樹叉太多了 怎麼辦?

取根節點的左右子樹!! 而且根據n的範圍 這樣剛好可以涵蓋所有情況 當然 如果 n ≥ 9 理論推測你可以取四棵樹

那就設左子樹節點...根節點 有 1個左 1個右 然後後面其他節點就只可能0/2

那初始狀態為1左(因為沒有左自然沒有右 根節點沒有左邊那一個就沒有右邊那一個)

設當前z總節點個數為a 左節點為cnt(count縮寫)個 右節點就有a - cnt - 1 個(1個根節點)

那顯而易見 左邊 cnt 個 右邊 a - cnt - 1 個的情況乘起來 就是總共a個的情況

原因 例如左邊 XOOOOO,OXOOOO....很多情況 則右邊OXXXXX,XOXXXX...... 總結點相同

但對於左邊每一種排列 右邊都有很多種不一樣的排列 只要節點總和相同 位置可以任意換 然後對於右邊也是 因此要乘 不是加!!

簡單理解一下: 兩個數列 長度均為6 每個數都不同 問你可能取到多少種不同的數的組合......6×6啊 多明顯......

然後對於每一種之前的情況 推到下一層只可能有一種 那就是這層某點有兩兒子 那麼就可以推出下層a個節點的情況——

         dp[a][dep] += dp[cnt][dep - 1] * dp[a - cnt - 1][dep - 1] 下行有對應的解釋=-=

該層a個點的情況||上層左邊cnt個點的情況||上層右邊a-cnt-1個點的情況

此處為什麼+=??

其實因為1個點的時候沒辦法找式子(根上面還有什麼呢) 我們初始化了一下=-=

for (int a = 1 ; a <= h ; a ++) dp[1][a] = 1;(別問我高度為什麼不用k要用h......)

這裡的1 就是推不到下一層的時候的情況——0

dp[a][dep]只會更新一次(看迴圈就知道了) 此處加的就是以上情況

說白了就是+1...

但for迴圈初始化是必須的 因為迴圈如果搜到了上面情況 0 × 0 = 0 再 + 0 = 0 那根本推不下去

這也是初始化之一~~

因此我們可以推到N個點 深度K的情況 此處注意——

求的是深度為K及比他淺的所有情況和

因此要減去深度比K淺的所有情況 [k - 1] 正好包括了那些所有情況

最後還有一個坑點 關於取膜♂取mod的事——

上層取模後的數可能比這層的大!!! 然而並不會大於9901

因此我懶得判情況 直接加上9901 和原來的數一起 然後取模即可

下放程式碼~

#include <iostream>
#include <cstdio>
#define mod 9901
using namespace std;
int dp[1 << 9][1 << 10];
int main() {
	int n,h;
	scanf("%d%d",&n,&h);
	for (int a = 1 ; a <= h ; a ++) dp[1][a] = 1;//預處理 1個點時所有深度的情況(只可能在深度1有1個點)
	for (int a = 3 ; a <= n ; a += 2)//總節點數迴圈
	for (int dep = 2 ; dep <= h ; dep ++)//每個節點的深度推導
	for (int cnt = 1 ; cnt < a ; cnt += 2) {//總結點相同時的不同情況 這裡左右並不是遍歷到終點 因為左右子樹互換也算一種情況..
		dp[a][dep] += dp[cnt][dep - 1] * dp[a - cnt - 1][dep - 1];//精華の更新
		dp[a][dep] %= mod;//取模
	}
	printf("%d\n",(dp[n][h] + mod - dp[n][h - 1]) % mod);//坑人的輸出
	return 0;
}

2.3.4 Money Systems貨幣系統

題目描述

母牛們不但建立了他們自己的政府而且選擇了建立了自己的貨幣系統(不愧於牛批這個詞)。

[In their own rebellious way],他們對貨幣的數值感到好奇。

傳統地,一個貨幣系統是由1,5,10,20 或 25,50, 和 100的單位面值組成的。

母牛想知道有多少種不同的方法來用貨幣系統中的貨幣來構造一個確定的數值。

舉例來說, 使用一個貨幣系統 {1,2,5,10,...}產生 18單位面值的一些可能的方法是:18x1, 9x2, 8x2+2x1, 3x5+2+1,等等其它。

寫一個程式來計算有多少種方法用給定的貨幣系統來構造一定數量的面值。

保證總數將會適合long long (C/C++) 和 Int64 (Free Pascal)。

INPUT FORMAT

貨幣系統中貨幣的種類數目是 V 。 (1<= V<=25)

要構造的數量錢是 N 。 (1<= N<=10,000)

第 1 行: 二整數, V 和 N
第 2 ..V+1行: 可用的貨幣 V 個整數 (每行一個 每行沒有其它的數)。

SAMPLE INPUT

3 10
1 2 5

OUTPUT FORMAT

單獨的一行包含那個可能的構造的方案數。

SAMPLE OUTPUT

10

炒雞經典的啦=-=

感覺很像多重揹包 就一個數可以取多次 然後和其他數(或者不和)組合 輸出能達到指定value的情況

不對 就是多重揹包=-=

那我們考慮每次遍歷一個"物品"的價值 因為怕更新到負數的地方 因此從物品價值開始更新

直到指定value 然後更新過程中每次能達到的value

即如果一個value可以通過以前的一個value加上物品價值得到 當前value就加上之前那個value的情況

此處揹包存的是到達某價值的情況數量啊 切記=-=

通過把每個物品遍歷一次 就可以得到所有的情況了

查詢O(1) 然而這種題目怎麼可能給你查詢多次2333

只用輸出第value個就好~(忽略0下標)

注意此處可能結果大於int=-= 用 long long 存......能儘量不用還是不用好 畢竟慢死

下放程式碼~

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
int v[1 << 5];
ll ans[5 << 11] = {1};
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for (int a = 1 ; a <= n ; a ++) scanf("%d",&v[a]);
	for (int a = 1 ; a <= n ; a ++)
		for (int b = v[a] ; b <= m ; b ++)
			ans[b] += ans[b - v[a]];
	printf("%lld\n",ans[m]);
	return 0;
}

---------------------------------------第三部分---------------------------------------

3.1.2 Score Inflation總分

題目描述

學生在我們USACO的競賽中的得分越多我們越高興(這就是你出水題的理由??)。

我們試著設計我們的競賽以便人們能儘可能的多得分,這需要你的幫助。

我們可以從幾個種類中選取競賽的題目,這裡的一個"種類"是指一個競賽題目的集合,解決集合中的題目需要相同多的時間並且能得到相同的分數。

你的任務是寫一個程式來告訴USACO的職員,應該從每一個種類中選取多少題目,使得解決題目的總耗時在競賽規定的時間裡並且總分最大。

輸入包括競賽的時間,M(1 <= M <= 10,000)(不要擔心,你要到了訓練營中才會有長時間的比賽)和N,"種類"的數目1 <= N <= 10,000。

後面的每一行將包括兩個整數來描述一個"種類":

第一個整數說明解決這種題目能得的分數(1 <= points <= 10000),第二整數說明解決這種題目所需的時間(1 <= minutes <= 10000)。

你的程式應該確定我們應該從每個"種類"中選多少道題目使得能在競賽的時間中得到最大的分數。

來自任意的"種類"的題目數目可能任何非負數(0或更多)。

計算可能得到的最大分數。

INPUT FORMAT

第 1 行: M, N--競賽的時間和題目"種類"的數目。
第 2-N+1 行: 兩個整數:每個"種類"題目的分數和耗時。

SAMPLE INPUT

300 4
100 60
250 120
120 100
35 20

OUTPUT FORMAT

單獨的一行包括那個在給定的限制裡可能得到的最大的分數。

SAMPLE OUTPUT

605
{從第2個"種類"中選兩題第4個"種類"中選三題}

和上題一樣 多重揹包

這裡存的是到達一個容量時的最大價值

懶得優化的我揹包後直接搜了=-=下放程式碼(最大點316ms真是羞恥)

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int ans[5 << 11],w[5 << 11],v[5 << 11];
int main()
{
	int time,m,tot = 0;
	scanf("%d%d",&time,&m);
	for (int a = 1 ; a <= m ; a ++) scanf("%d%d",&v[a],&w[a]);
	for (int a = 1 ; a <= m ; a ++)
		for (int b = w[a] ; b <= time ; b ++)
		ans[b] = max(ans[b],ans[b - w[a]] + v[a]);
	for (int a = 10000 ; a > 0 ; a --)
	if (ans[a]) tot = max(ans[a],tot);
	printf("%d\n",tot);
	return 0;
}

3.1.6 Stamps郵票

題目描述

已知一個 N 枚郵票的面值集合(如,{1 分,3 分})和一個上限 K —— 表示信封上能夠貼 K 張郵票。計算從 1 到 M 的最大連續可貼出的郵資。

例如,假設有 1 分和 3 分的郵票;你最多可以貼 5 張郵票。很容易貼出 1 到 5 分的郵資(用 1 分郵票貼就行了),接下來的郵資也不難:

  • 6 = 3 + 3
  • 7 = 3 + 3 + 1
  • 8 = 3 + 3 + 1 + 1
  • 9 = 3 + 3 + 3
  • 10 = 3 + 3 + 3 + 1
  • 11 = 3 + 3 + 3 + 1 + 1
  • 12 = 3 + 3 + 3 + 3
  • 13 = 3 + 3 + 3 + 3 + 1。

然而,使用 5 枚 1 分或者 3 分的郵票根本不可能貼出 14 分的郵資。因此,對於這兩種郵票的集合和上限 K=5,答案是 M=13。

INPUT FORMAT

第 1 行: 兩個整數,K 和 N。K(1 <= K <= 200)是可用的郵票總數。N(1 <= N <= 50)是郵票面值的數量。
第 2 行 .. 檔案末: N 個整數,每行 15 個,列出所有的 N 個郵票的面值,面值不超過 10000。

SAMPLE INPUT

5 2

1 3

OUTPUT FORMAT

 

第 1 行:

一個整數,從 1 分開始連續的可用集合中不多於 K 張郵票貼出的郵資數。

SAMPLE OUTPUT

13

一看就知道是動規了 感覺也很像......揹包??

這裡求的是可能得到的數的集合

如果一個個列舉肯定會爆 最大值兩百萬 你還要列舉每個組合 還有好多重複的......

感覺像揹包 實際上......應該是吧 給了總的容量 然後自己搭配

因為此處求的是組合個數 就是可能的價值個數 dp陣列怎麼能存容量呢 

於是dp陣列存價值 然後價值一樣的時候 容量就可能不一樣了

dp是找最優嘛 那麼相同價值 容量越小越好 不是嗎

那我們因此dp陣列初始賦極大值(好吧兩百也行) 然後價值為0時容量為0

列舉每個價值 能否通過之前的某種狀態 加上這個價值得到當前狀態

然後當前狀態容量與更新後的比較 如果更小就更新 因為可以裝更多 豈不美哉~~

如果裝到一個地方容量大於指定的了 說明大包溢位了 直接退出~

(一開始我以為中間可能有斷層還開了tot一個個記 結果發現並沒有必要 全是連著的啊啊啊)

大概思路就這樣 下放程式碼

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = 2000010;
int ans[1 << 21],v[1 << 6],tot;
int main()
{
	int n,m;
	memset(ans,0x7f,sizeof(ans));
	ans[0] = 0;
	scanf("%d%d",&n,&m);
		for (int a = 1 ; a <= m ; ++ a) scanf("%d",&v[a]);
		for (int a = 1 ; a <= MAX ; ++ a)
		{
			for (int b = 1 ; b <= m ; b ++)
				if (a - v[b] < 0) continue; else
				ans[a] = min(ans[a],ans[a - v[b]] + 1);
			if (ans[a] > n)
			{
				printf("%d\n",a - 1);
				break;
			}
		}
	return 0;
}

3.2.2 Stringsobits__01串

題目描述

考慮排好序的N(N<=31)位二進位制數。

你會發現,這很有趣。因為他們是排列好的,而且包含所有可能的長度為N且含有1的個數小於等於L(L<=N)的數。

你的任務是輸出第I(1<=I<=長度為N的二進位制數的個數)大的,長度為N,且含有1的個數小於等於L的那個二進位制數。

INPUT FORMAT

共一行,用空格分開的三個整數N,L,I。

SAMPLE INPUT

5  3  19

OUTPUT FORMAT

共一行,輸出滿足條件的第I大的二進位制數。

SAMPLE OUTPUT

10011

[特典]樣例解釋---->滿足條件的二進位制數有(從小到大排列)——

0,1,10,11,100,101,110,111,1000,1001,1010,1011,1100,1101,1110,10000,10001,10010,10011

0也是啊 發現沒有 發現沒有!!!

我天=-=這個動規我愣是沒想出來......

好了正題...這題給了你位數 要你求這個位數的所有位數的數的集合裡 從小到大排第 i 個 且 含有 1 的數量小於等於 L 的數

分析 每個位數只有兩個狀態 0 和 1 從第一位開始狀態可初始化 我們考慮一維給位數

然後題目要求含有 1 的數量 考慮存不同狀態時 1 的 個數 那二維就給他了

狀態怎麼轉移??

首先頭肯定是 1 的了 不然......就怪怪的 對 就怪怪的

但是本題動規就是按照有頭為 0 來考慮的!!! 怪怪的 對 怪怪的 那為什麼呢

因為頭為 0 的時候 就可以推到前一個(更少)的位數了啊 顯而易見

設推到某處時 i 位數 1 有 j 個 則此時要更新的是 dp[ i ][ j ] (由於本人看見 [j 難受 於是加了空格變成[ j )

我們從上一位數的狀態推過來 上一位數的狀態則是 dp[ i - 1 ] 那麼 j 呢??

按照慣例 肯定是上一位數的所有狀態 加上這次更新後的多的狀態啦

上一位數的所有狀態怎麼辦呢? 我們把更新後的數分兩種情況討論

第一種 就是繼承了之前的所有狀態的 但沒有新的狀態 那怎麼得到呢??

前面加 0 !!! 此時儲存的狀態總數肯定是不變的 (因為數都沒變就加了前導 0 ) 但位數變了 就是dp[ i ][ j ] = dp[ i - 1 ][ j ]

第二種 就是這一位數可能為1 此時可能多出好多種情況 但 i 和 j 要和上面的相符

因此 就求上一位的 1 的個數少 1 情況 就是加上這一位的 1 後

1 的總數為 j 的情況 即 dp[ i ][ j ] = dp[ i - 1 ][ j - 1 ]

當然 兩者要加起來 就是 dp[ i ][ j ] = dp[ i - 1 ][ j ] + dp[ i - 1 ][ j - 1 ]

這就是遞推式啦~~ 然後根據遞推式的範圍 別忘了預處理喲~

當然本題除了遞推式 查詢輸出也是重點......

首先他位數給出來了呀233 直接暴力加上之前的字首搜應該也是行的 就從100000000......開始搜 初始化已經得到了dp[ N ][ L ] 然後遇到一個符合的打進去一個 搜到第 i 個退出即可 (僅僅理論 懶死的冰海醬不想打)

但我們考慮比較哲♂學的方法

還是要利用字首暴力........其實也動了點腦筋的 判斷此時第 N 位數是 0 還是 1 這種演算法嘛 讓吾油來教你好啦——

我們看回遞推式 是把當前位數為 0 和 為 1 的 兩種情況加起來得到 然後推下去的 然後得到第 i 個數就是答案

那我們能不能以第 i 個數的位置 和他的長度 倒推回去呢 顯然可以的 我們知道第 i 個數是第幾個 也知道這個位數的第一個數是第幾個(就是dp[ i ][ j - 1 ]) 但推到後面可能遇到中間有 00 000 之類的情況 因此 每次更新 如果此時 i ≥ dp[ i ][ j - 1 ] 就說明這個位數是 1 反之是 0 如果是 1 則相應的減去此時的狀態數量(dp[ i ][ j - 1 ]) 然後更新( 減 i ) 如果是0就不用減了 直接更新

每次更新記得輸出當前位數是 0 還是 1 哦 沒必要存數組裡了啦~

下放程式碼(最近比較喜歡縮行2333~)

#include <iostream>
#include <cstdio>
using namespace std;
int dp[1 << 6][1 << 6];
long long n;
int l,k;
int main()
{
	scanf("%d%d%lld",&l,&k,&n);
	for (int a = 0 ; a <= l ; dp[a++][0] = 1);
	for (int a = 0 ; a <= k ; dp[0][a++] = 1);
	for (int a = 1 ; a <= l ; ++ a)
	for (int b = 1 ; b <= l ; ++ b)
	dp[a][b] = dp[a - 1][b] + dp[a - 1][b - 1];
	for (int a = l - 1 ; a >= 0 ; -- a)
	if (dp[a][k] < n) putchar('1'),n -= dp[a][k],--k;
	else putchar('0');putchar('\n');
	return 0;
}

3.3.2 Shopping Offers商店購物

題目描述

在商店中,每一種商品都有一個價格(用整數表示)。例如,一朵花的價格是 2 zorkmids (z),而一個花瓶的價格是 5z 。為了吸引更多的顧客,商店舉行了促銷活動。促銷活動把一個或多個商品組合起來降價銷售,例如:

  • 三朵花的價格是 5z 而不是 6z,
  • 兩個花瓶和一朵花的價格是 10z 而不是 12z。

對於上面的商品資訊,購買三朵花和兩個花瓶的最少花費是:以優惠價購買兩個花瓶和一朵花(10z),以原價購買兩朵花(4z)。

編寫一個程式,計算顧客購買一定商品的花費,儘量利用優惠使花費最少。儘管有時候新增其他商品可以獲得更少的花費,但是你不能這麼做。

INPUT FORMAT

輸入檔案包括一些商店提供的優惠資訊,接著是購物清單。

 

第一行

優惠商品的種類數(0 <= s <= 99)。
 

第二行..第s+1 行

每一行都用幾個整數來表示一種優惠方式。第一個整數 n (1 <= n <= 5),表示這種優惠方式由 n 種商品組成。後面 n 對整數 c 和 k 表示 k (1 <= k <= 5)個編號為 c (1 <= c <= 999)的商品共同構成這種優惠,最後的整數 p 表示這種優惠的優惠價(1 <= p <= 9999)。優惠價總是比原價低。
 

第 s+2 行

這一行有一個整數 b (0 <= b <= 5),表示需要購買 b 種不同的商品。
 

第 s+3 行..第 s+b+2 行

這 b 行中的每一行包括三個整數:c ,k ,和 p 。c 表示唯一的商品編號(1 <= c <= 999),k 表示需要購買的 c 商品的數量(1 <= k <= 5)。p 表示 c 商品的原價(1 <= p <= 999)。最多購買 5*5=25 個商品。

SAMPLE INPUT

2
1 7 3 5
2 7 1 8 2 10
2
7 3 2
8 2 5

OUTPUT FORMAT

只有一行,輸出一個整數:購買這些物品的最低價格。

SAMPLE OUTPUT

14

完全揹包=-= 但是好麻煩啊啊啊......

一開始考慮鄰接存優惠陣列 然後過濾掉不能用的

結果瘋狂爆 0 =-= 無奈之下翻標程

沒一個是這樣的 =-= 突然發現省這麼點空間沒卵用

然後照搬了下來 =-= 真是羞愧......

這題總共可能買的商品最多五種 好的 那麼直接五維陣列走起

dp[6][6][6][6][6] 由於下標 0 懶得轉換

對於狀態轉換 5個迴圈列舉 然後裡面套一個可用優惠的迴圈

現在狀態 = min(當前狀態 , (現在 - 當前優惠涉及的n種商品 的數量)的狀態 + 優惠價)

直接更新 然後有些 0 的......沒必要判 直接算進去也是可以的

這題真的是麻煩...... 然後為了壓行後的程式碼更加美觀 我又壓了一下2333......下放

#include <algorithm>
#include <cstdio>
using namespace std;
int dp[6][6][6][6][6],p[105][1005],num[105],orip[105],a[6],b[6];
struct trades {
	int id,k,r;
} t[6];
int main()
{
	int s,n,c,k;
	scanf("%d",&s);
	for (int i = 1 ; i <= s ; ++ i)
	{
		scanf("%d",&num[i]); for (int j = 1 ; j <= num[i] ; ++ j)
		scanf("%d%d",&c,&k),p[i][c] = k;
		scanf("%d",&orip[i]); // 吶 美觀吧 我for都壓上去了
	}
	scanf("%d",&n); for (int i = 1 ; i <= n ; ++ i)
	scanf("%d%d%d",&t[i].id,&t[i].k,&t[i].r); //吶 美觀吧
	for (a[1] = 0; a[1] <= t[1].k ; ++ a[1])
	for (a[2] = 0; a[2] <= t[2].k ; ++ a[2])
	for (a[3] = 0; a[3] <= t[3].k ; ++ a[3])
	for (a[4] = 0; a[4] <= t[4].k ; ++ a[4])
	for (a[5] = 0; a[5] <= t[5].k ; ++ a[5]) //吶 美觀吧
	{
		int tot = 0; //為了防止裡面某幾行太長 這裡加了個定義=-=
		for (int i = 1 ; i <= 5 ; ++ i) tot += a[i] * t[i].r;
		for (int i = 1 ; i <= s ; ++ i)
		{
			if (num[i] > n) continue;
			for (int j = 1 ; j <= n ; ++ j) b[j] = a[j] - p[i][t[j].id];
			if (b[1] < 0 || b[2] < 0 || b[3] < 0 || b[4] < 0 || b[5] < 0) continue;
			tot = min(tot,dp[b[1]][b[2]][b[3]][b[4]][b[5]] + orip[i]);//醜死了這四行=-=
		}
		dp[a[1]][a[2]][a[3]][a[4]][a[5]] = tot;
	}
	printf("%d\n",dp[t[1].k][t[2].k][t[3].k][t[4].k][t[5].k]);
	return 0;
}

3.3.4 Home on the Range家的範圍

題目描述

農民約翰在一片邊長是N (2 <= N <= 250)英里的正方形牧場上放牧他的奶牛。

(因為一些原因,他的奶牛隻在正方形的牧場上吃草。)

遺憾的是,他的奶牛已經毀壞一些土地。( 一些1平方英里的正方形)

農民約翰需要統計那些可以放牧奶牛的正方形牧場(至少是2x2的,在這些較大的正方形中沒有小於1x1的部分被分割毀壞)。

你的工作要在被供應的資料組裡面統計所有不同的正方形放牧區域(>2x2)的個數。

當然,放牧區域可能是重疊。

INPUT FORMAT

 

第 1 行:

 

  N,牧區的邊長。

 

第 2 到 n+1行:

 

N個沒有空格分開的字元。

 

0 表示 "那一個區段被毀壞了";1 表示 " 準備好被吃"。

SAMPLE INPUT

6
101111
001111
111111
001111
101101
111001

OUTPUT FORMAT

輸出那些存在的正方形的大小和個數,一種一行。

SAMPLE OUTPUT

2 10
3 4
4 1

直接搜尋爆了 QwQ 還剩一個點是這樣的 n = 250 而且全是 1 的特殊情況=-=

於是加了個O2(大罪大孽) 784ms過了=-=

但為了弄透我去搜了百度 然後發現......動規

好吧原本就覺得是動規就是懶得打

先放放暴力搜尋的方法 (原創但是原創這種題目的解法並不厲害=-=)

並沒有任何實際用處 大忙人們請跳過~~

最主要的還是判斷重疊的正方形如何疊加 這裡我想了好久=-= 先來看個例子(沒加顏色很尷尬)

Tip:原圖就是讀入噠~

這裡為什麼要減去呢OwO 因為之前算過的正方形已經包含了

怎麼減去呢~~呵 我的o陣列用來表示當前點為左上角的正方形的長度的

然後如果當前點更新了 新的o值大於原o值 就加上 怎麼加呢

圖例(第二個和第三個):o值大了 那大的部分就是沒算過的 圖中某處由2變成4 那3和4就是新增的

只算3和4 然後推到相鄰的 下面和右邊的點(原本想直接更新結果發現太麻煩 像上圖六個地方重疊)

講得不詳細=-=還是看程式吧 (喂喂你這黑廝..)

#pragma GCC optimize(2)//溫馨提示:這句是空的 聰明人都看不見
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = (1 << 8) - 1;
int th[MAX][MAX],ans[MAX],o[MAX][MAX];
inline void r(int &x)//神奇讀入
{
	char q = getchar();
	while (q != '0' && q != '1') q = getchar();
	if (q == '0') x = 0; else x = 1;
}
int pd(int x,int y,int len)
{//第一個for:比較新增的y列的每一個數 如果為0(廢土地)就退出
	for (int a = x ; a < x + len ; ++ a) if (!th[a][y + len - 1]) return 0;
	for (int a = y ; a < y + len ; ++ a) if (!th[x + len - 1][a]) return 0;
	return 1;//第二個for:比較新增的x列的每一個數 如果為0(廢土地)就退出
}//這裡th的第[x+len-1][y+len-1]個判斷了兩次..其實可以把第二個迴圈條件改成 y+len-1 然而用處不大
void update(int x,int y,int len)
{
	if (o[x][y] >= len) return;//如果原o值大於等於將修改的邊長為len的正方形的值 說明之前的計算已經包含了 直接退出
	for (int a = len ; a > o[x][y] ; ++ans[a--]);//當前計算未包含 加上
	o[x][y] = len;//更新o值
	update(x + 1,y,len - 1);//繼續推
	update(x,y + 1,len - 1);//繼續推
}
void fs(int x,int y)//原本想打dfs覺得不像,bfs也不像,然後打了fs...就是搜尋
{
	int a = 0;//初始化當前正方形邊長
	while (pd(x,y,++a));//邊長每次加1 然後判斷...見那個子程式
	if (--a < 2) return;//因為邊長增加到判斷到廢土地的那一行 要-1 然後邊長小於2的不計 兩句合併
	update(x,y,a);//正方形左上角及邊長已確定 開始更新
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a)
	for (int b = 1 ; b <= n ; ++ b) r(th[a][b]);//讀入點
	for (int a = 1 ; a < n ; ++ a)
	for (int b = 1 ; b < n ; ++ b) fs(a,b);//以每個點為左上角慢慢搜尋更新
	for (int a = 2 ; a <= n ; ++ a) if (ans[a]) printf("%d %d\n",a,ans[a]);//輸出答案
	return 0;
}

好了好了正經一點 這是dp專題 我們考慮dp (其實我也不信有人會看我那不開O2不特判一千八ms的東西)

然後我發現那...動規居然和我的有意取童♂工之妙

嗯哼 dp[a][b]表示以該處為右下角 得到的最大的正方形

為了防止世界被破壞被重疊的正方形干擾 我們要取以該點往上和往左能延伸的最小邊長 取min

然後每個點遍歷一遍 把2到該點值都加一遍到ans中 原理和本人的暴力差不多

好啦大概就是這樣 下放拍過的程式碼~

#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAX = (1 << 8) - 1;
int th[MAX][MAX],dp[MAX][MAX],ans[MAX];
inline void r(int &x)
{
	char q = getchar();
	while (q != '0' && q != '1') q = getchar();
	if (q == '0') x = 0; else x = 1;
}
int main()
{
	int n;
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a)
	for (int b = 1 ; b <= n ; ++ b) r(th[a][b]);
	for (int a = 1 ; a <= n ; ++ a)
	for (int b = 1 ; b <= n ; ++ b)
	if (th[a][b]) dp[a][b] = min(dp[a - 1][b - 1],min(dp[a - 1][b],dp[a][b - 1])) + 1;
	for (int a = 2 ; a <= n ; ++ a)
	for (int b = 2 ; b <= n ; ++ b)
	for (int p = 2 ; p <= dp[a][b] ; ++ p) ++ans[p];
	for (int a = 2 ; a <= n ; ++ a) if (ans[a]) printf("%d %d\n",a,ans[a]);
	return 0;
}

吶 你看 同樣的思路 別人dp的就是快 32ms過了 =-= 而且程式碼又短又好看

3.3.5 A Game遊戲

題目描述

有如下一個雙人遊戲:N(2 <= N <= 100)個正整數的序列放在一個遊戲平臺上,兩人輪流從序列的兩端取數,取數後該數字被去掉並累加到本玩家的得分中,當數取盡時,遊戲結束。以最終得分多者為勝。

編一個執行最優策略的程式,最優策略就是使自己能得到在當前情況下最大的可能的總分的策略。你的程式要始終為第二位玩家執行最優策略。

INPUT FORMAT

第一行: 正整數N, 表示序列中正整數的個數。
第二行至末尾: 用空格分隔的N個正整數(大小為1-200)。

SAMPLE INPUT


4 7 2 9

5 2

OUTPUT FORMAT

只有一行,用空格分隔的兩個整數: 依次為玩家一和玩家二最終的得分。

SAMPLE OUTPUT

18 11

這篇文章超好的 解釋也很詳細 看懂了就別回來了

(剛開始) 哇 遊戲啊 NIM 還是 SG 啊 然而我都沒學啊......

(心驚膽戰地看完題後) 其實我的內心一直毫無波動 =-= 上一行是騙人的 就是個區間dp而已

然後突然想起 某奶牛零食(洛谷的=-=) 去翻了一下 發現我沒過 =-= 不行太羞恥了 打完這部分就去做

區間dp 大都是 取小區間內的最優決策 然後推到大區間 就是從 (1,1) (2,2)....到 (1,2) (2,3)...直到(1,n)

小區間裡最優決策初始化 就是隻有一個單位的地方 如 (1,1) 初始化很好記 大都這樣

然後這題和奶牛零食一類的 都是隻能取區間兩端的點 那很好推嘛 看看奶牛零食

從 (1,1) 推到 (1,2) 就是 (1,1) + v[2] × 2 和 (2,2) + v[1] × 2 兩者的最大值

從 (1,2) 推到 (1,3) 就是 (1,2) + v[3] × 3 和 (2,3) + v[1] × 3 兩者的最大值

吶 大概就是這樣 但這題呢 是兩個人共同取的數列啊=-= 他讓你維護第二個最優 然而事實上也要維護第一個最優 =-=

初始化就是尋常的在單點的狀態上啦 dp[a][a] = v[a] 這個毋容置疑

然後怎麼推上去呢 先看看正(cuo)常(wu)思維

當推到2的時候 他只能選一個 然後他又是先手 那就可以選二者之中最大的

第 1 2 個的dp值都是一個點的最大權值 =-=

然後三個選兩個 就某兩個裡面最大的加上個另外一個

四個選三個 裡面三個最大的...加上? 不就成了四選三了嗎??嗯 是的 因此不能這麼推 =-=

那我們考慮先手拿了這個 後手拿了這個......等等 我們為什麼要這麼推 這不就是模擬了嗎

花了我一個下午終於發現了 (呵 笨死了的Frocean 還四維生物呢) ......dp完全是用來存先手的值的 並沒確定哪個是先手 只是題目給了 然後我用了這個條件 從而一直糾結 僅此而已 =-=

設 dp[a][b] 為 a 到 b 的區域內 先手能取得的最大的值

怎麼推呢 由題意 易得只有兩種狀態會轉移到該狀態 分別是 dp[a+1][b] 和 dp[a][b-1] 為了方便 例子就用第一個吧 =-=

吶 先手在 dp[a][b] 中選了第a個 那後手就是這個區間減去第a個 就是 dp[a+1][b] 的區間中選

然後再之前一個狀態 dp[a+1][b] 就是先手啦 求出來了 那個狀態之前的狀態可能是 dp[a+2][b] 或 dp[a+1][b-1]

然後這個又是後手 但在他減 1 的 區間裡面 他又是先手 以此類推

因此 dp[a][b]都存的是當前先手取得的值的 所以算到第 dp[1][n] 的時候 存的就是該測試點的先手取得的值

好了最麻煩的概念清楚了 開始推 (例子依然用上面的)

首先我們可以理所當然地得出後手在這區域內的和 sum - dp[a+1][b] 然後加上點權 v[a] 就是第一張情況

然後第二種情況 就是 sum - dp[a][b-1] 然後加上點權 v[b]

sum 一維的點權字首和 注意 求 a 到 b ( a ≤ b ) 的區域內 是 sum[b] - sum[a - 1] 因為 sum[a] 包含第a個數 不能減去

然後最優肯定是最大值啦 所以推導式就是——

dp[a][b] = max(sum[b] - sum[a] - dp[a + 1][b] + v[a] , sum[b - 1] - sum[a - 1] - dp[a][b - 1] + v[b])

再次提醒 sum要注意 縱使怪處極多 但我真的沒錯 =-= 下放程式碼~

#include <algorithm>
#include <cstdio>
using namespace std;
const int MAX = 105;
int dp[MAX][MAX],v[MAX],sum[MAX],n;
int main()
{
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a) scanf("%d",&v[a]);
	for (int a = 1 ; a <= n ; ++ a) dp[a][a] = v[a];
	for (int a = 1 ; a <= n ; ++ a) sum[a] = sum[a - 1] + v[a];
	for (int len = 1 ; len < n ; ++ len)
	for (int h = 1,t = h + len ; h <= n - len ; ++ h,++ t)
	{
		int p1 = sum[t] - sum[h] - dp[h + 1][t] + v[h];
		int p2 = sum[t - 1] - sum[h - 1] - dp[h][t - 1] + v[t];
		dp[h][t] = max(p1,p2);
	}
	printf("%d %d\n",dp[1][n],sum[n] - dp[1][n]);
	return 0;
}

3.4.4 Raucous Rockers“破鑼搖滾”樂隊

題目描述

你剛剛繼承了流行的“破鑼搖滾”樂隊錄製的尚未發表的N(1 <= N <= 20)首歌的版權。你打算從中精選一些歌曲,發行M(1 <= M <= 20)張CD。每一張CD最多可以容納T(1 <= T <= 20)分鐘的音樂,一首歌不能分裝在兩張CD中。

不巧你是一位古典音樂迷,不懂如何判定這些歌的藝術價值。於是你決定根據以下標準進行選擇:

  • 歌曲必須按照創作的時間順序在CD盤上出現。
  • 選中的歌曲數目儘可能地多。

     

INPUT FORMAT

第一行: 三個整數:N, T, M.
第二行: N個整數,分別表示每首歌的長度,按創作時間順序排列。

SAMPLE INPUT

4 5 2 4 3 4 2

OUTPUT FORMAT

一個整數,表示可以裝進M張CD盤的樂曲的最大數目。

SAMPLE OUTPUT

3

開始直接暴力 =-= 然而聽說可以用dp??

細細想想 這些盤可裝可不裝 然後裝盤有容量......不是很像揹包嗎

於是就是揹包了 =-= 冰海醬爽快地確認道

然後讀入的歌的長度當做容積 而且沒有權重 (或者為 1 ) 這類揹包都是要把物品放最外層的

因此 邊讀入邊更新就好

還有 為了防止一次更新加上許多個該種物品 要倒推 這個看推導式自行腦補

dp存歌曲數量哈 因為題目要求輸出這個嘛 然後dp[a][b] 表示第 a 個盤 裝了 b (請在此處停頓) 分鐘 最大的歌曲數

考慮推導方法——

第一種 就是不加 就是不加 =-= dp[a][b] = dp[a][b]

第二種 加 而且沒滿 那麼 dp[a][b] = dp[a][b - len] + 1 然後 len是歌曲長度 加 1 是歌曲數量

第三種 加 而且滿了 那麼 dp[a][b] = dp[a - 1][t] + 1 然後 t 就是題目輸入的 CD容量上限 因為反正你都裝不下了 肯定要取上個碟的最大值 那你肯定懶得判斷 直接找上界嘛 反正推的時候會更新到 (這就是揹包的好處)

最後懶得判斷滿沒滿 反正求最大 便三種一起算 同時為了美(suo)觀(hang) 第二種和第三種套一個 max 外面第一種再套個 即可

下放dp程式碼~~ (暴力太醜就丟掉啦)

#include <algorithm>
#include <cstdio>
using namespace std;
int dp[22][22],ans;
int main()
{
	int n,t,m,len;
	scanf("%d%d%d",&n,&t,&m);
	while (n--)
	{
		scanf("%d",&len);
		for (int a = m ; a > 0 ; -- a)
		for (int b = t ; b >= len ; -- b)
		dp[a][b] = max(dp[a][b],max(dp[a - 1][t],dp[a][b - len]) + 1);
	}
	printf("%d\n",dp[m][t]);
	return 0;
}

---------------------------------------第四部分---------------------------------------

4.1.1 Beef McNuggets麥香牛塊

題目描述

農夫布朗的奶牛們正在進行鬥爭,因為它們聽說麥當勞正在考慮引進一種新產品:麥香牛塊。奶牛們正在想盡一切辦法讓這種可怕的設想泡湯。奶牛們進行鬥爭的策略之一是“劣質的包裝”。“看,”,奶牛們說,“如果你用只有一次能裝3塊、6塊或10塊的三種包裝盒裝麥香牛塊,你就不可能滿足想要一次只想買1、2、4、5、7、8、11、14或17塊麥香牛塊的顧客了。劣質的包裝意味著劣質的產品。”

你的任務是幫助這些奶牛。給出包裝盒的種類數N(1<=N<=10)和N個代表不同種類包裝盒容納麥香牛塊個數的正整數(1<=i<=256),輸出顧客不能用上述包裝盒(每種盒子數量無限)買到麥香牛塊的最大塊數。如果在限定範圍內所有購買方案都能得到滿足,則輸出0。

範圍限制是所有不超過2,000,000,000的正整數。

INPUT FORMAT

第1行: 包裝盒的種類數N
第2行到N+1行: 每個種類包裝盒容納麥香牛塊的個數

SAMPLE INPUT

3
3
6
10

OUTPUT FORMAT

輸出檔案只有一行數字:顧客不能用包裝盒買到麥香牛塊的最大塊數或0(如果在限定範圍內所有購買方案都能得到滿足)。

SAMPLE OUTPUT

17

做這題氣得我想吃麥香牛塊

我一個盒子幹嘛要裝滿啊真的是

好了正題 =-=

首先數論知識 兩個數 p 和 q 且 gcd(p,q) = 1 (互質)

那麼無法表示為兩數的 某個倍數 (可以不等) 的 最大的 數 為 p × q - p - q

雖然我也不太懂 =-= 我只知道範圍開到 1 << 18 (就是65536 即256 × 256) 多一點就好

然後如果答案大於 1 << 18 答案換成 0

這是個坑點啊啊啊 有個資料 4 252 250 254 256 答案為 0 如果不判的話 答案根據你的範圍隨機 (神tm)

好了 這題就是個無盡完全揹包 不過dp只用存 能不能到達某個容量 初始化 dp[0] = 1 不用解釋了吧 =-=

然後更新 我們用 或 ( | ) 即可 就判斷兩個裡面 有一個 1 就更新 然後 1 遇到 0 就不更新

當然用 max 也可以 if 也可以 喜歡就好 但這樣最快~~ 下放程式碼~~

#include <cstdio>
using namespace std;
const int MAX = 65600;
int dp[MAX + (1 << 4)] = {1},ans;
int main() {
	int n,v;
	scanf("%d",&n);
	for (int a = 1 ; a <= n ; ++ a) {
	scanf("%d",&v);//邊讀邊更新 上一題有 不解釋 =-=
		for (int b = v ; b <= MAX ; ++ b)
		dp[b] |= dp[b - v];//或運算
	}
	for (int a = MAX ; a > 0 ; -- a)
		if (!dp[a]) {
		ans = a;
		break;
		}
	if (ans > (1 << 16)) ans = 0;//重要的判斷
	printf("%d\n",ans);
	return 0;
}

相關推薦

USACO 動態規劃 彙總

資料結構能打到省選了=-= 但是其他類問題只能打到普及 普及啊啊啊!!! 而且這邊省選組都是什麼仙人掌啊,什麼系什麼點對啊...感覺資料結構並沒有什麼用 (實際上很有用但我不會運用) 然後頹到提高組來了..結果全是模擬還有一堆沒學的其他玩意 (迴文自動機) 資料結構也沒

USACO3.1.6 Stamps郵票

這一道題,算是我“千辛萬苦”才做出來的吧。因為,我在時超70後代寫了很久,才找出了一個能對但又不超時的方法。 下邊,來說一說我的超時思想: 用i來列舉,從1一直到2000000,在一個個判斷,知道i不可以結合出來就輸出並結束程式,(呵呵,不超時的話,那就是相當於中了2億元的大獎了) 好了看一

USACO3.2.2 Stringsobits__01串

【題目】 考慮排好序的N(N<=31)位二進位制數。 你會發現,這很有趣。因為他們是排列好的,而且包含所有可能的長度為N且含有1的個數小於等於L(L<=N)的數。 你的任務是輸出第I(1<=I<=長度為N的二進位制數的個數)大的,長度為N,且含有1的個數小於等於L的那

USACO3.2.6 Sweet Butter香甜的黃油

這一題,我本來是想用spfa演算法的,但後來嫌太累了,就打了個習以為常的floyd演算法。 floyd演算法很簡單——三重迴圈。。。 for (k=1;i<=p;i++) for (i=1;j<=p;j++) for (j=i

JZOJ1312.USACO5.1.1 Fencing the Cows圈奶牛

problem 題目描述 農夫約翰想要建造一個圍欄用來圍住他的奶牛,可是他資金匱乏。他建造的圍欄必須包括他的奶牛喜歡吃草的所有地點。對於給出的這些地點的座標,計算最短的能夠圍住這些點的圍欄的長度。 PROGRAM NAME: fc INPUT FORMA

USACO3.1.4 Shaping Regions形成的區域

題目描述 N個不同的顏色的不透明的長方形(1 <= N <= 1000)被放置在一張寬為A長為B的白紙上。 這些長方形被放置時,保證了它們的邊於白紙的邊緣平行。 所有的長方形都放置在白紙內

JZOJ1274.USACO2.4.4 Bessie Come Home回家

題目描述 現在是晚餐時間,而母牛們在外面分散的牧場中。 農民約翰按響了電鈴,所以她們開始向穀倉走去。 你的工作是要指出哪隻母牛會最先到達穀倉(在給出的測試資料中,總會有且只有一隻速度最快的母牛)。 在擠奶的時候(晚餐前),每隻母牛都在她自己的牧場上,一些

一大堆的福利之USACO Mixing Milk混合牛奶

題目描述 牛奶包裝是一個如此低利潤的生意,所以儘可能低的控制初級產品(牛奶)的價格變的十分重要。 請幫助快樂的牛奶製造者(Merry Milk Makers)以可能的最廉價的方式取得他們所需的牛

一大堆的福利之USACOThe Clocks

考慮將如此安排在一個 3 x3 行列中的九個時鐘: |-------| |-------| |-------| | | | | | | | |---O | |---O | | O | | | |

Luogu關卡2-15動態規劃的背包問題(2017年10月)

splay image 說明 方案 理解 ostream img 如果 一次 任務說明:這是最基礎的動態規劃。不過如果是第一次接觸會有些難以理解。加油闖過這個坎。 P1060 開心的金明 小明的媽媽給小明N元錢,小明想買m件物品,每個物品價值為 價格*重要度,求出不超過N元

習題詳解動態規劃DP:硬幣遊戲 蛋糕

動態規劃DP硬幣蛋糕塔 硬幣 題目描述 農夫約翰的奶牛喜歡玩硬幣遊戲,因此他發明了一種稱為“Xoinc”的兩人硬幣遊戲。 初始時,一個有N(5 <= N <= 2,000)枚硬幣的堆疊放在地

習題詳解動態規劃:線性DP

最長上升子序列LIS 問題:求一串數字的最長上升自序列 設f[i]為以第i位數字結尾的子序列的最大長度.當我們列舉i時,我們需要從左往右列舉i前面的數字j,若數字j小於數字i則說明數字i可以在以j結尾的序列之後,因此f[j]+1是一種方案數.若沒有比i小的時

寒江雪動態規劃小結

前言   大概是從5月11號開始在賽碼網上刷題,這個網站上有的題目樣例覆蓋面不全。當然了,不能過於強求,能有這麼個網站練題還是挺好的。練題無論在哪練目的是為了通過題目提升自己.   相比於POJ和HDU等各大知名OJ而言,還是比較簡單了。所以還是建議到POJ上

OCP最新CUUG OCP 12c 071考試題(67

ctu ans The evaluate edi cit edit where which 67、(25-8)choose the best answer: View the Exhibit and examine the structure of CUSTOMERS ta

OCP最新CUUG OCP 12c 071考試題(65

answer oos choose pla limit tasks sel task 不能 65、(22-16) choose the best answer: The CUSTOMERS table has the following structure: You nee

OCP最新CUUG OCP 12c 071考試題(68

數據 ted name 性能視圖 statement 不能訪問 inf dynamic acc 68、(29-13)choose two: Which two statements are true? (Choose two.) A) DICTIONARY is a vie

LeetCode動態規劃(上篇共75

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【5】 Longest Palindromic Substring 給一個字串,需要返回最長迴文子串 解法:dp[i][j] 表示 s[i..j] 是否是迴文串,轉移方程是

LeetCode & 劍指offer刷動態規劃與貪婪法2:14 剪繩子

【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) 14 剪繩子 題目:給你一根長度為n的繩子,請把繩子剪成m段   (m和n都是整數,n>1並且m>1) 每段繩子的長度記為k[0],k[1],...,k[m].請問k[0]*k

LeetCode & 劍指offer刷動態規劃與貪婪法1:70 Climbing Stairs

【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) 70. Climbing Stairs You are climbing a stair case. It takes   n   steps to reach t

LeetCode & 劍指offer刷動態規劃與貪婪法5:Maximum Product Subarray

【LeetCode & 劍指offer 刷題筆記】目錄(持續更新中...) Maximum Product Subarray Given an integer array  nums , find the contigu