日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[JLOI2015]战争调度

發(fā)布時間:2023/12/3 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [JLOI2015]战争调度 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 題目
  • 題解
  • 代碼實現(xiàn)

題目

臉哥最近來到了一個神奇的王國,王國里的公民每個公民有兩個下屬或者沒有下屬,這種關(guān)系剛好組成一個 n 層的完全二叉樹。

公民 i 的下屬是 2 * i 和 2 * i +1。最下層的公民即葉子節(jié)點的公民是平民,
平民沒有下屬,最上層的是國王,中間是各級貴族。

現(xiàn)在這個王國爆發(fā)了戰(zhàn)爭,
國王需要決定每一個平民是去種地以供應(yīng)糧食還是參加戰(zhàn)爭,
每一個貴族(包括國王自己)是去管理后勤還是領(lǐng)兵打仗。

一個平民會對他的所有直系上司有貢獻(xiàn)度,
若一個平民 i 參加戰(zhàn)爭,他的某個直系上司 j 領(lǐng)兵打仗,
那么這個平民對上司的作戰(zhàn)貢獻(xiàn)度為 wij。

若一個平民i 種地,他的某個直系上司 j 管理后勤,
那么這個平民對上司的后勤貢獻(xiàn)度為 fij,
若 i 和 j 所參加的事務(wù)不同,則沒有貢獻(xiàn)度。

為了戰(zhàn)爭需要保障后勤,國王還要求不多于 m 個平民參加戰(zhàn)爭。
國王想要使整個王國所有貴族得到的貢獻(xiàn)度最大,并把這件事交給了臉哥。
但不幸的是,臉哥還有很多 deadline 沒有完成,他只能把這件事又轉(zhuǎn)交給你。
你能幫他安排嗎?

輸入格式
第一行兩個數(shù) n;m。

接下來 2^(n-1) 行,每行n-1 個數(shù),第 i 行表示編號為 2^(n-1)-1+ i 的平民對其n-1直系上司的作戰(zhàn)貢獻(xiàn)度,其中第一個數(shù)表示對第一級直系上司,即編號為 (2^(n-1)-1+ i)/2 的貴族的作戰(zhàn)貢獻(xiàn)度 wij,依次往上。

接下來 2^(n-1)行,每行n-1個數(shù),第i行表示編號為 2^(n-1)-1+ i的平民對其n-1個直系上司的后勤貢獻(xiàn)度,其中第一個數(shù)表示對第一級直系上司,即編號為 (2^(n-1)-1+ i)/2 的貴族的后勤貢獻(xiàn)度 fij ,依次往上。

輸出格式
一行一個數(shù)表示滿足條件的最大貢獻(xiàn)值

輸入輸出樣例
輸入
3 4
503 1082
1271 369
303 1135
749 1289
100 54
837 826
947 699
216 389
輸出
6701
說明/提示
對于 100% 的數(shù)據(jù),2 <= n <= 10,m <= 2n 1,0 <= wij ;fij <= 2000

題解

首先這道題光輸入就難到了一大片人
可以這么輸入w[i][j]w[i][j]w[i][j],f[i][j]f[i][j]f[i][j]表示i號葉子節(jié)點的第j層長官共同去打仗或種地時的價值


接著題目告訴了是一棵完全二叉樹,就應(yīng)該長這樣

在這里就有一個小技巧:每一個葉子節(jié)點在每一層都只會有一個與之相關(guān)的長官
所以這個關(guān)系應(yīng)該是一條單鏈
舉例說明:v1的價值貢獻(xiàn)于root和f1的選擇打仗或者種地有關(guān),
而f2干什么這樣應(yīng)該就能明白了吧!!!


之后我們再來想,因為每一個葉子節(jié)點的貢獻(xiàn)我們需要找到每一層它的長官的抉擇
所以這個狀態(tài)我們必須要帶著走,肯定有很多親故會想到狀壓DP,
其實根本不需要,因為我們采用狀壓DP,
是因為后面的狀態(tài)會多次用到前面的某一個狀態(tài)
而前面提到每一個葉子節(jié)點的貢獻(xiàn)是有各自的單鏈決定的,
彼此之間的狀態(tài)是不會沖突的
所以我們根本就不需要專門用一維去儲存狀態(tài),
我們可以選擇在dfs時把狀態(tài)一起傳下去即可,


對于狀態(tài)轉(zhuǎn)移方程式,我們可以換個角度轉(zhuǎn)移,
每一個節(jié)點(除開葉子節(jié)點)都有且只有左右兒子,
就用DP[i][j]DP[i][j]DP[i][j]表示到i節(jié)點為止,一共有j個士兵參于打仗
DP[i][j+k]=DP[num<<1][j]+DP[num<<1∣1][k]DP[i][j+k]=DP[num<<1][j]+DP[num<<1|1][k]DP[i][j+k]=DP[num<<1][j]+DP[num<<11][k]
我們直接暴力枚舉i的左右兒子各有多少個士兵參加戰(zhàn)斗,更新出i參加戰(zhàn)爭的士兵
蒟蒻覺得這個方法轉(zhuǎn)移很妙!!

代碼實現(xiàn)

我解釋一下vis的巧妙用法,
為什么可以直接用vis把這一層k都直接賦值
這又要用到上面解釋的每一層v都只有一個有關(guān)的長官
所以我就直接用這一層為0/1表示這一個長官是打仗還是種田
根本沒有必要去找到那個長官具體是誰,

這就是dfs的好處,一條鏈找到底,完成狀態(tài)更新后才改變狀態(tài)
dfs也就決定了每一次重新改變狀態(tài)的時候,都要清空上一次的狀態(tài)值

#include <cstdio> #include <iostream> using namespace std; #define MAXN 1500 int n, m, result; int w[MAXN][15], f[MAXN][15], dp[MAXN][MAXN]; bool vis[15];void solve ( int num, int k ) {for ( int i = 0;i <= ( 1 << k );i ++ )dp[num][i] = 0;if ( ! k ) {for ( int i = 1;i <= n;i ++ )if ( vis[i] )dp[num][1] += w[num][i];elsedp[num][0] += f[num][i];return;}vis[k] = 0;solve ( num << 1, k - 1 );solve ( num << 1 | 1, k - 1 );for ( int i = 0;i <= ( 1 << ( k - 1 ) );i ++ )for ( int j = 0;j <= ( 1 << ( k - 1 ) );j ++ )dp[num][i + j] = max ( dp[num][i + j], dp[num << 1][i] + dp[num << 1 | 1][j] );vis[k] = 1;solve ( num << 1, k - 1 );solve ( num << 1 | 1, k - 1 );for ( int i = 0;i <= ( 1 << ( k - 1 ) );i ++ )for ( int j = 0;j <= ( 1 << ( k - 1 ) );j ++ )dp[num][i + j] = max ( dp[num][i + j], dp[num << 1][i] + dp[num << 1 | 1][j] ); } int main() {scanf ( "%d %d", &n, &m );n --;for ( int i = 0;i < ( 1 << n );i ++ )for ( int j = 1;j <= n;j ++ )scanf ( "%d", &w[i + ( 1 << n )][j] );for ( int i = 0;i < ( 1 << n );i ++ )for ( int j = 1;j <= n;j ++ )scanf ( "%d", &f[i + ( 1 << n )][j] );solve ( 1, n );for ( int i = 0;i <= m;i ++ )result = max ( result, dp[1][i] );printf ( "%d", result );return 0; }

總結(jié)一下感受,vis,dp的定義真的用的很巧妙

總結(jié)

以上是生活随笔為你收集整理的[JLOI2015]战争调度的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。