my_动态规划
步驟:
1.動態規劃的哪種類型(1.背包(選和不選) ,2.區間dp(小區間推大區間) 3.線性dp? , 4.樹形dp)
+普通dp? ?or? ?判定dp 哪個好實現(復習時若該題用判定,思考為啥不用普通)
2.狀態表示及其含義(先看數據范圍)(也可聯想最終狀態來輔助)
(2,3步驟)可以顛倒,有時推出從誰轉移可以得到狀態表示
3.當前狀態從誰轉移而來 / 當前狀態可以推導那些狀態?->轉移方程
4.處理邊界+初始化
記搜都可以轉化為dp,所以寫dp時可以先用記搜想一想,而dp不一定都能轉化為記搜
兩種寫法記搜(遞歸)dp(遞推)
dp初始化為負極大值大多是因為避免無效轉移,參考失衡天平,,dp也有初始化為極大值的
關于狀態壓縮:如果當前狀態從前i-1轉移而來,可以直接壓掉表示i的那一維
? ? ? ? ? ? ? ? ? ? ? ? ?如果當前狀態只從第i-1位轉移而來,第一維要保留,但可以滾動數組優化
01背包模板,這個也看一下吧,選前i個的寫法,不能忘其根本***(但是做題不建議加上那一維,除非題目一定要嚴格維護第i-1種狀態,而不是前i-1種狀態)若硬要加,先看(洛谷)NASA的食物計劃
補充:
?01背包原本的狀態轉移方程式:
dp[i][j]=dp[i-1][j],(所以dp[i][j]本質上是從前i-1轉移而來)
,dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+c[i])
易發現當前狀態只與上一個(本質上是前i-1個)有關,所以可以用滾動數組優化,用一維數組存儲上一個(i-1)的值
然后用i來更新上一個的值,但我們更新時當前物品a[i]只能選一次,正序可能會導致物品選多次,逆序則保證前面的dp[j]存儲的是上個狀態最大值,最多用一次i
但是01背包精髓還是在于二維的01背包的狀態轉移方程式(對于前i個的理解)
(洛谷)采藥(01背包模板)
#include <iostream> #include <algorithm> using namespace std; #define ll long long int t,m,w[101],c[101],f[1001]; int main() {scanf("%d%d",&t,&m);for(int i=1;i<=m;i++)scanf("%d%d",&w[i],&c[i]);for(int i=1;i<=m;i++){for(int j=t;j>=w[i];j--){f[j]=max(f[j],f[j-w[i]]+c[i]);}}printf("%d",f[t]);return 0; }1.kkksc03考前臨時抱佛腳(01背包)
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <stdlib.h> using namespace std; int s[5],sub[21],f[21][1200],t1,v; long long tot; int main() {scanf("%d%d%d%d",&s[1],&s[2],&s[3],&s[4]);for(int i=1;i<=4;i++){v=0;t1=0;memset(f,0,sizeof(f));//memset(sub,0,sizeof(sub));for(int j=1;j<=s[i];j++){scanf("%d",&sub[j]);v+=sub[j];//總共需要花多少時間 }for(int j=1;j<=s[i];j++)//物品個數 {for(int k=1;k<=v/2;k++)//背包容量,,使背包容量盡量==v/2,使得左右腦所耗時間基本相等 {if(k<sub[j])//模板 f[j][k]=f[j-1][k];else{f[j][k]=max(f[j-1][k-sub[j]]+sub[j],f[j-1][k]);//價值與重量相等,背包容量為時間 t1=max(t1,f[j][k]);//算出背包容量為v/2的時候最多的價值,,即最多的時間 }}}tot+=max(t1,v-t1); }cout<<tot;return 0; }2.(洛谷)5倍經驗日
#include <iostream> using namespace std; long long lose[2000],win[2000],use[2000]; long long f[2000]; int main() {int n,x;scanf("%d%d",&n,&x);for(int i=1;i<=n;i++){scanf("%d%d%d",&lose[i],&win[i],&use[i]);}for(int i=1;i<=n;i++){for(int j=x;j>=0;j--){long long tem=f[j];//如果所剩藥的數量夠,比較(打敗,輸掉,之前f[j]的值)的情況 if(j>=use[i]) //如果藥不夠,比較(打敗,之前f[j]的值)的情況 f[j]=max(f[j],f[j-use[i]]+win[i]);f[j]=max(f[j],tem+lose[i]); }}printf("%lld",5*f[x]);return 0; }(牛客)牛牛的旅游紀念品(基于01背包的背包dp)(理解選前i個和選第i個區別)***(求最值)
(牛客)音量調節(01背包變形+判定dp)(嚴格從第i-1種狀態轉移,不能壓維度,可用滾動數組) (求最值)
(牛客)簡單的煩惱(01背包+貪心)(求最值)
(牛客)美味菜肴(01背包+貪心:推公式)(求最值)
(牛客)codeforces(跟上一題一樣)
(洛谷)最大約數和(很隱蔽的01背包)(求最值)
(洛谷)有線電視網(樹形dp+分組背包+01背包)
(洛谷)集合 Subset Sums(背包求方案數)(方案數**)
(洛谷)找啊找啊找GF(三重費用dp,一遍ac)(求最值)
(洛谷)搭配購買(01背包+并查集)(求最值)
(洛谷)yyy2015c01 的 U 盤(01背包+二分答案)(求最值)
? 完全背包(跟01差不多,只是第i件物品能選無限次)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
(洛谷)瘋狂的采藥(完全背包模板)
?
(洛谷)四方定理(背包dp(完全背包)) (求方案數**)
(洛谷)A+B Problem(再升級)(隱蔽的完全背包思想)(求方案數*)
(洛谷)質數和分解
(牛客)貨幣系統(完全背包變形+思維+判定dp)
(洛谷)投資的最大效益
(洛谷)Buying Hay S(題目理解,細節處理***)
(洛谷)紀念品(經典題目+狀態壓縮+倒序的完全背包)
(洛谷)飛揚的小鳥(細節題+難想的完全背包+二維完全背包寫法)(需要再看看)
(洛谷)Cut Ribbon(完全背包+要求背包剛好裝滿)
線性dp
(牛客)舔狗舔到最后一無所有
#include <iostream> #include <cstring> #include <algorithm> using namespace std; #define int long long const int N=100010,mod=1e9+7; int f[3][N];// f[0/1/2][i]前i天且第i天去第0/1/2家購買方案 // f[0][i]=(f[1][i-1]+f[2][i-1]+f[1][i-2]+f[2][i-2]) // f[1][i]=(f[0][i-1]+f[2][i-1]+f[0][i-2]+f[2][i-2]) // f[2][i]=(f[1][i-1]+f[0][i-1]+f[1][i-2]+f[0][i-2])signed main() {int T;cin>>T;f[0][0]=f[1][0]=f[2][0]=0;f[0][1]=f[1][1]=f[2][1]=1;f[0][2]=f[1][2]=f[2][2]=3;for(int i=3;i<N;i++){f[0][i]=(f[1][i-1]+f[2][i-1]+f[1][i-2]+f[2][i-2])%mod;f[1][i]=(f[0][i-1]+f[2][i-1]+f[0][i-2]+f[2][i-2])%mod;f[2][i]=(f[1][i-1]+f[0][i-1]+f[1][i-2]+f[0][i-2])%mod;}while(T--){int n;cin>>n;cout<<(f[0][n]%mod+f[1][n]%mod+f[2][n]%mod)%mod<<endl;} }我們發現第i天去哪一家都是一樣的,3家關系是相同的,因此可以縮小至一維,只要在初始值乘3,得到的結果也是原來的三倍#include <iostream> #include <cstring> #include <algorithm> using namespace std; #define int long long const int N=100010,mod=1e9+7; int f[N]; signed main() {int T;cin>>T;f[0]=0;f[1]=3;f[2]=9;for(int i=3;i<N;i++)f[i]=(f[i-1]*2%mod+f[i-2]*2%mod)%mod;while(T--){int n;cin>>n;cout<<f[n]<<endl;} }(牛客)烏龜棋
(牛客)失衡天平(經典)
(牛客)購物
?(牛客)隊伍配置(經典)
(洛谷)守望者的逃離(非傳統dp)
(洛谷)擺花(方案數)
(洛谷)線段(特殊的定義狀態方法)
(牛客)釘子和小球 (由當前狀態推導其他狀態)
(牛客)可愛の星空(很像區間dp的dfs)?
(洛谷)尼克的任務(線性dp,由當前狀態推導其他狀態,初始化為負極大值)
(牛客)打鼴鼠(特殊的定義狀態方法)
(牛客)被3整除的子序列(線性dp)
(牛客)刪括號(字符串dp不算很懂)
(洛谷)編輯距離(字符串dp,體現動態規劃無后效性,很妙)
(洛谷)子串 (字符串dp+滾動數組)(似懂非懂)
(洛谷)字串距離(字符串dp)(有點啟發)
(洛谷)傳球游戲(提示:循環順序即遞推順序,要嚴謹!!!)
(洛谷)雷濤的小貓(嚴謹循環順序,一遍ac)
(洛谷)最大正方形(題解的:很妙,但特殊,自己寫的:線性dp+稍微有點復雜的前綴和)
(洛谷)最大正方形II (跟上一題的差不多,特殊)
(牛客)禪(簡單線性dp)
(洛谷)逆序對數列(前綴和+dp) 錯因:對逆序對不熟)
(洛谷)英雄聯盟(循環順序,循環方向因遵循推導方向,特別是壓縮前i-1狀態時,不能忘了推導方向)
(牛客)斗地主(取模,需要注意的地方)
(牛客)美麗序列(精準狀態定義)
(牛客)小紅的子序列(通過上一題有感而發,寫出來)
(洛谷)覆蓋墻壁(藍橋杯原題)#include <iostream> using namespace std; #define ll long long ll n,f[2000000],sum[2000000];//f[i]為填滿前2*i個方格的總方案數,sum為前綴和 int main() {cin>>n;f[0]=f[1]=1;f[2]=2,f[3]=5;sum[0]=1,sum[1]=2,sum[2]=4,sum[3]=9;//初始化 for(int i=4;i<=n;i++){f[i]=sum[i-1]+sum[i-3];f[i]%=100000;sum[i]=(sum[i-1]+f[i])%100000;}cout<<f[n]%10000;return 0;} /*考慮放在最后那一部分 1.放2*1磚塊(豎放)方案數:f[i-1] 2.放2個2*1磚塊(橫放)方案數:f[i-2]//這里不考慮豎放是因為會重復:f[i-1]最后一個本來就是豎放然后f[i]也豎放 3.放一個L型磚塊(因為可翻轉,所以要乘2) { 1.再放一個L型磚塊(剛好互補)方案數:f[i]=f[i-3] 2.放一個2*1型磚塊(豎放)再放一個L型磚塊 方案數:f[i]=f[i-4] 3.2×1 的磚塊可以交替著放下去,再補上一個 L 型磚塊,從而消去這個突出,直到 2*1 磚塊和 L 型磚塊恰好填滿墻壁(f[0]) } 所以總方案數:f[i-1]+f[i-2]+2*sum[i-3]=sum[i-1]+sum[i-3]?滾動數組模板(牛客)音量調節
#include <iostream> #include<cstring> #include <algorithm> using namespace std; #define inf 0x3f3f3f3f int n,be,maxx; bool dp[2][2000]; int main() { // memset(dp,-inf,sizeof dp);// cout<<dp[1];cin>>n>>be>>maxx;dp[0][be]=1;for(int i=1;i<=n;i++){int x;cin>>x;bool flag=false;for(int j=0;j<=maxx;j++){if(dp[i&1^1][j])//i&1只有奇和偶兩種情況{if(j>=x)dp[i&1][j-x]=1,flag=true;if(j+x<=maxx)dp[i&1][j+x]=1,flag=true;dp[i&1^1][j]=false;//要把之前的i-1初始化回去,(如果下一次能把上上一次的覆蓋掉則沒關系)}}if(!flag){cout<<-1;return 0;}}int ans=-1;for(int j=0;j<=maxx;j++){if(dp[n&1][j])ans=j;}cout<<ans;return 0; }(雙線程)方格取數(牛客)
#include <iostream> #include <algorithm> using namespace std; int ma_p[11][11],dp[11][11][11][11]; long long ans=0; int main() {int n;cin>>n;while(1)//輸入 {getchar();int x,y,z;scanf("%d%d%d",&x,&y,&z);if(x==y&&y==z&&z==0)break;else ma_p[x][y]=z;}for(int a=1;a<=n;a++)//到點(a,b),(c,d)的路徑的和for(int b=1;b<=n;b++)for(int c=1;c<=n;c++)for(int d=1;d<=n;d++){if(a==c&&b==d)//若兩點重合{dp[a][b][c][d]=dp[a-1][b][c][d-1]+ma_p[a][b];}else{int tem=0;if(dp[a-1][b][c-1][d]>tem)tem=dp[a-1][b][c-1][d];//四種情況,因為到某個點的兩條路徑步數一定相等if(dp[a-1][b][c][d-1]>tem)tem=dp[a-1][b][c][d-1];//所以兩個點必須同時動if(dp[a][b-1][c-1][d]>tem)tem=dp[a][b-1][c-1][d];if(dp[a][b-1][c][d-1]>tem)tem=dp[a][b-1][c][d-1];dp[a][b][c][d]=tem+ma_p[a][b]+ma_p[c][d];}}printf("%d",dp[n][n][n][n]);return 0; }(洛谷)傳紙條?
拓撲+dp
(洛谷)旅行計劃
(洛谷)綠豆蛙的歸宿(期望dp+拓撲)
最長公共子序列
(洛谷)最長公共子序列(直接看代碼)
樸素算法;
當s1==s2,dp[i][j]=dp[i-1][j-1]+1
當s1[i]!=s2[j],dp[i][j]=max(dp[i-1][j],dp[i][j-1])
樸素算法o(n^2)超時。。。 #include<iostream> using namespace std; int dp[20000][20000],arr1[200000],arr2[200000]; int main() {int n,maxn=0;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&arr1[i]);for(int i=1;i<=n;i++)scanf("%d",&arr2[i]);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(arr1[i]==arr2[j])dp[i][j]=dp[i-1][j-1]+1;else{dp[i][j]=max(dp[i-1][j],dp[i][j-1]);}if(dp[i][j]>maxn)maxn=dp[i][j];}}printf("%d",maxn);return 0; }離散化,轉化為最長上升子序列o(nlogn),只能用于A串各元素互不相同的情況給它們重新標個號:把3標成a,把2標成b,把1標成c,,于是變成: A: a b c d e B: c b a d e 出現一個性質,只要這個子序列在B中單調,則它就是A的子序列,所以問題轉化成求最長上升子序列 #include <iostream> #include <unordered_map> #include <algorithm> using namespace std; unordered_map<int,int>mark; int n,arr2[200000],d[200000],len=1; int main() {scanf("%d",&n);for(int i=1;i<=n;i++){int x;scanf("%d",&x);mark.insert(pair<int,int>(x,i));//用一個數組記錄一下每個數字變成了什么,相當于離散化了一下,3-1;2-2;1-3;4-4;5-5 }for(int i=1;i<=n;i++)scanf("%d",&arr2[i]);for(int i=1;i<=n;i++){arr2[i]=mark[arr2[i]];}d[len]=arr2[1];for(int i=2;i<=n;i++){if(arr2[i]>d[len])d[++len]=arr2[i];else{int x=lower_bound(d+1,d+1+len,arr2[i])-d;d[x]=arr2[i];}}printf("%d",len);return 0; }多重背包
樸素版本(將所有物品看成單獨的物品,轉化成01背包)O(n^3)
二進制優化版本(因為任何一個數都可以由二進制數轉化而成,eg:選1-9個該物品可一轉化成從1,2,4,6,8里的數字組合,從而轉化成01背包)O(n^2logn)
void multi_knapsack2(int n,int W)//二進制拆分 {//W為背包容量,,w[i],c[i],p[i]為第i件物品的重量,價值,數量for(int i=1;i<=n;i++){if(p[i]*w[i]>=W)//轉化成完全背包,因為裝不完for(int j=w[i];j<=W;j++)dp[j]=max(dp[j],dp[j-w[i]]+c[i]); else{for(int k=1;p[i]>0;k<<=1)//k=2的0 1 2 3.。。次方,p[i]是物品一開始的數量也是余數 {int x=min(k,p[i]);//選較小的數, eg 8=1+2+4+1for(int j=W;j>=w[i]*x;j--)//轉化成01背包dp[j]=max(dp[j],dp[j-w[i]*x]+x*c[i]);p[i]-=x;//余數 } }}}(洛谷)櫻花
洛谷)砝碼稱重
(洛谷)Space Elevator 太空電梯(排序+多重背包)
分組背包(洛谷:通天之分組背包)
#include<iostream> #include <algorithm> #include <cstring> using namespace std; int t,n,m,k,dp[1111],ans,w[1111],z[1111],g[1111][1111],b[1111]; int main() {cin>>m>>n;for(int i=1;i<=n;i++){int x;cin>>w[i]>>z[i]>>x;b[x]++;//記錄該組的物品數 t=max(t,x);//記錄一共有多少組g[x][b[x]]=i;//記錄第x組中第b[x]個物品的編號 }for(int i=1;i<=t;i++){for(int j=m;j>=0;j--)//枚舉容量,巧妙避開了物品沖突 {for(int k=1;k<=b[i];k++)//枚舉第i組的物品{if(j>=w[g[i][k]])dp[j]=max(dp[j],dp[j-w[g[i][k]]]+z[g[i][k]]);} }}cout<<dp[m];return 0; }(洛谷)有線電視網(樹形dp+分組背包+01背包)?
最長(上升和非上升)公共子序列,,直接看代碼
(洛谷)導彈攔截
求最長非上升公共子序列和最長上升公共子序列 記住允許=的(非上升,非下降)用upper,不允許(遞增,遞減)用lower,剛好反過來 #include <iostream> #include <cmath> #include <algorithm> using namespace std; #define ll long long int arr[100007],d1[100007],d2[100007],len1=1,len2=1; ll ans; inline int read() {int a=0,b=1;char ch=getchar();while(ch<'0'||ch>'9'){b*=-1;ch=getchar();}while(ch>='0'&&ch<='9'){a=(a<<3)+(a<<1)+(ch^48);ch=getchar();}return a*b; } int main() {int n=0;while(scanf("%d",&arr[++n])!=EOF);n--;//這里雖然是先++的,但最后還要--d1[1]=arr[1],d2[1]=arr[1];//用d1來存最長不上升序列 for(int i=2;i<=n;i++){if(d1[len1]>=arr[i])d1[++len1]=arr[i];//如果a[i] <= d[len],說明a[i]可以接在d后面(而整個d還是有序的),那就簡單粗暴地把a[i]丟進delse //如果a[i]>d1[len1],在d中找到第一個小于a[i]的數,刪了,用a[i]替代它 ,就類似前面不選該數{int x=upper_bound(d1+1,d1+len1+1,arr[i],greater<int>())-d1;//greater<int>()把序列變成遞增,upper_bound找第一個小于a[i]的數 d1[x]=arr[i];//存放最長非降序公共子序列 }if(d2[len2]<arr[i])d2[++len2]=arr[i];else{int x=lower_bound(d2+1,d2+len2+1,arr[i])-d2; d2[x]=arr[i];//存放最長上升公共子序列 }}cout<<len1<<endl<<len2;return 0; }(牛客)合唱隊形(最長遞增和最長遞減)?
(洛谷)木棍加工(最長遞增子序列+貪心,且有點小特殊)
(洛谷)友好城市(很隱晦的最長上升子序列)
區間dp
規律:區間dp狀態定義? ?一定是要開兩個以上維度維護左區間和右區間,eg;dp[i][j]維護(i,j)區間
dp[i1][j1][i2][j2]同時維護(i1,j1),(i2,j2)區間
(洛谷)合并石子(弱化版)
區間dp,,一般版本O(n^3),一般不會被卡時 #include <iostream> #include <cstring> #include <algorithm> using namespace std; int arr[302],f[302][302],sum[302]; int main() {int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",arr+i);sum[i]=sum[i-1]+arr[i];}memset(f,0x7f,sizeof(f));//把初始值設為無窮大***for(int len=1;len<=n;len++)//枚舉區間長度 ,,因為不知道區間長度多少合適(有點類似迭代加深搜索){for(int i=1;i+len-1<=n;i++)//左端 {int j=i+len-1;//右端 if(len==1)f[i][j]=0;else{for(int k=i;k<j;k++)//枚舉中間值,通過小區間推大區間(k不能==j,否則k+1>j){int tem=f[i][k]+f[k+1][j]+sum[j]-sum[i-1];//合并i-k和k+1-j和[i-k]-[(k+1)-j]石子的費用 if(f[i][j]>tem)f[i][j]=tem;}}}}printf("%d",f[1][n]);return 0; }區間dp,記憶性搜索版本,復雜度>一般版本,一般也不會被卡時,怎么方便怎么來 include <iostream> #include <cstring> using namespace std; #define maxn 0x7f7f7f7f int arr[302],f[302][302],sum[302]; int dfs(int L,int R) {if(L==R)return f[L][R]=0;//返回0的同時將f[L][R]值賦為0if(f[L][R]!=maxn)return f[L][R];//算過就返回 for(int k=L;k<=R;k++){int tem=dfs(L,k)+dfs(k+1,R)+sum[R]-sum[L-1];//小區間推大區間,算大區間時,小區間不一定有值,所以再調用dfs if(f[L][R]>tem)f[L][R]=tem;} return f[L][R]; } int main() {int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",arr+i);sum[i]=sum[i-1]+arr[i];}memset(f,0x7f,sizeof(f));//將f初始值設為0x7f7f7f7f dfs(1,n);printf("%d",f[1][n]);return 0; }(牛客)取數游戲2(線性dp+區間dp思想:大區間推小區間)
(牛客)合并回文子串(線性dp+區間dp思想+字符串dp+判定dp)
(洛谷)乘積最大
(洛谷)刪數(區間dp思想)
(洛谷)玩具取名(區間dp(稀有的判定性),且有點難想)
(洛谷)加分二叉樹(區間dp+二叉樹的中序遍歷轉前序遍歷)
環形區間dp
(牛客)石子合并
(牛客)凸多邊形的劃分?
#include <iostream> #include <algorithm> #include <cstring> using namespace std; #define ll long long ll arr[300],sum[300]; ll dp1[300][300],dp2[300][300]; int main() {int n;cin>>n;for(int i=1;i<=n;i++)cin>>arr[i],sum[i]=sum[i-1]+arr[i];for(int i=n+1;i<=2*n-1;i++)sum[i] = sum[i - 1] + arr[i - n];memset(dp1,0x3f,sizeof dp1);for(int len=1;len<=n;len++){for(int i=1;i+len-1<=2*n-1;i++){int j=i+len-1;if(len==1)dp2[i][j]=dp1[i][j]=0;else{for(int k=i;k<j&&j<=2*n-1;k++){dp1[i][j]=min(dp1[i][j],dp1[i][k]+dp1[k+1][j]+sum[j]-sum[i-1]);dp2[i][j]=max(dp2[i][j],dp2[i][k]+dp2[k+1][j]+sum[j]-sum[i-1]);} }}}ll maxn=-1000000;ll minn=0x3f3f3f3f;for(int i=1;i<=n;i++){maxn=max(maxn,dp2[i][i+n-1]);minn=min(minn,dp1[i][i+n-1]);}cout<<minn<<endl<<maxn;return 0; }樹形dp
先dfs子節點的狀態,在從下往上推根節點狀態
1.(牛客)沒有上司的舞會
#include <iostream> #include <vector> using namespace std; int n,root=-1,myroot[7000],happy[7000],dp[7000][2];//dp[i][j],i表示節點序號,j為0或者1,表示選或不選該節點 vector<int>son[7000]; void tree_dfs(int u) {dp[u][0]=0;dp[u][1]=happy[u];for(int i=0;i<son[u].size();i++){int v=son[u][i];tree_dfs(v);dp[u][1]+=dp[v][0];//若選該結點,則不選其子節點dp[u][0]+=max(dp[v][0],dp[v][1]);//若不選該結點}return; } int main() {scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",happy+i);}for(int i=1;i<=n;i++){int l,k;cin>>l>>k;if(l==0&&k==0)break;myroot[l]=1;//有父節點son[k].push_back(l);}for(int i=1;i<=n;i++)//找沒有父節點的,即根節點{if(!myroot[i]){root=i;break;}}tree_dfs(root);cout<<max(dp[root][1],dp[root][0]);return 0; }(洛谷)最大子樹和(很簡單的樹形dp)
(洛谷)聯合權值(不一樣的樹形dp***)?
(洛谷)有線電視網(樹形dp+分組背包(01))經典模板
(牛客)Treepath(樹形dp)
(牛客)月之暗面(樹形dp+后置維護)(沒想出來)
狀壓dp
(牛客)互不侵犯king
狀壓DP-互不侵犯_嗶哩嗶哩_bilibili(直接看代碼也可以。。。)
#include <iostream> #include <algorithm> using namespace std; #define ll long long ll dp[10][1<<9][1000];//第i行,第i行為st的擺放方式,放了k個國王的擺放方案數 int n,k; int c(ll st)//返回放了st代表的國王數 {int sum=0;while(st){if(st%2)sum++;st/=2;}return sum; } bool check1(ll st)//判斷當行是否合法 {for(int i=0;i+1<n;i++){if(st & (1<<i) && (st & (1<<(i+1))))return false;//&兩邊為真則為真}return true; } bool check2(ll st,ll st2)//判斷當行于其上一行間關系是否合法 {for(int i=0;i<n;i++){if(st & (1<<i)){if(st2 & (1<<i))return false;else if(i+1<n && (st2 & (1<<(i+1)))) return false;else if(i-1<n && (st2 & (1<<(i-1)))) return false;}}return true; } int main() {scanf("%d %d",&n,&k);for(int i=1;i<=n;i++){for(int st=0;st<(1<<n);st++)//從0開始,為什么st不是<=?因為0-n-1就有9個數了{if(!check1(st))continue;//若該行擺法方式違法if(i==1)dp[i][st][c(st)]=1;else{for(int j=c(st);j<=k;j++)//枚舉1~i行一共放了的國王數{for(int st2=0;st2<(1<<n);st2++)//枚舉上一行放國王位置情況{if(check2(st,st2)&&check1(st2)){dp[i][st][j]+=dp[i-1][st2][j-c(st)];}}}}}}ll ans=0;for(int i=0;i<(1<<n);i++)ans+=dp[n][i][k];//cout<<ans;return 0; }總結
- 上一篇: VM16虚拟机去虚拟化心得4
- 下一篇: 微信小程序 使用TGit 实现版本控制