1. 程式人生 > 其它 >laravel js 單張圖片上傳及預覽

laravel js 單張圖片上傳及預覽

主要總結一下樹形dp的一些難點與細節問題。

什麼是樹形dp

顧名思義,就是長得像樹結構的dp,在樹上進行的dp。

而樹形dp有一個比較套路化的狀態定義,就是定義 \(dp[i]\) 表示以 \(i\) 為根結點的子樹的最大/最小值。

一般的樹形dp用dfs實現。

裸樹形dp

這裡推薦幾道比較裸的樹形dp:

1.P1122 最大子樹和

2.P1352 沒有上司的舞會這道題很重要,它代表了一類帶分類的樹形dp,即多一維記錄這個狀態的一些資訊(因為會有影響)

3.P2016 戰略遊戲

4.P1131 [ZJOI2007] 時態同步有難度的裸題(

5.P2585 [ZJOI2006]三色二叉樹和沒有上司的舞會有點像的一道題

具體不詳細講,給一個模板(最大子樹和)

int dfs(int x,int fa){//為無根樹就多了一個fa
	dp[x]=a[x];
	for(ri i=0;i<T[x].size();i++){
		int y=T[x][i];
		if(y==fa)continue;
		dfs(y,x);
		if(dp[y]>0)dp[x]+=dp[y];
	}
	ans=max(ans,dp[x]);
}

樹上揹包

這個詳細地講一下吧。

樹上揹包可以說是有依賴性的dp,因為它的子樹與子樹之間的 \(dp\) 值是有關係的。

於是我們可以把它看作一個揹包來處理。

1.P2015 二叉蘋果樹

珂愛的小模板。

樹上揹包有三重迴圈,分別為子節點,容量,和給每一個子樹分配的容量。

注意是無根樹!

int dfs(int x,int fa){
	int sum=0;
	for(ri i=0;i<T[x].size();i++){
		int y=T[x][i].pos;
		if(y==fa)continue;
		sum=sum+dfs(y,x)+1;
		for(ri j=min(sum,q);j>=0;j--){//容量 
			for(ri k=0;k<=j-1;k++){//分配的樹枝數量 
				dp[x][j]=max(dp[x][j],dp[y][k]+dp[x][j-k-1]+T[x][i].w);
			}
		}
	}
	return sum;
}

2.P2014 [CTSC1997]選課

這道題Wrasar印象深刻,因為當時口胡了一個錯的程式碼(連方程都是錯的),然後交上去AC了(((

講一下正解和一些注意的點吧qwq

(1)輸出的答案是 \(dfs(0,n+1)\) ,因為建樹的時候把所有的沒有先修課的課程都算作先修課為0的課。

所以可以把0看作一個根結點,而本來的可以說是很多棵樹,有了0就像一個森林了是不是。

(2)初始值設為 \(dp[x][1]=s[x];\) ,其中 \(s\) 用來存對應的學分數。

這個好想吧。

(3)計算結點數的 \(sum\) 要先賦為 \(1\)

因為至少有0這個點了(不過是虛擬的)。

(4)方程是 \(dp[x][j]=max(dp[x][j],dp[y][k]+dp[x][j-k]);\)

這個也是顯然的(

然後就差不多了,程式碼放一下。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define ll long long
#define ri register int
using namespace std;
const int MAXN=310;
int n,m,k,s[MAXN],dp[MAXN][MAXN];
struct node{
	int pos,sco;
};
vector <node> T[MAXN];

int dfs(int x,int fa){
	dp[x][1]=s[x];
	int sum=1;
	for(ri i=0;i<T[x].size();i++){
		int y=T[x][i].pos;
		if(y==fa)continue;
		sum+=dfs(y,x);
		for(ri j=min(sum,m);j>=0;j--){
			for(ri k=0;k<=j-1;k++){
				dp[x][j]=max(dp[x][j],dp[y][k]+dp[x][j-k]);
			}
		}
	}
	return sum;
}

int main() {
	ios::sync_with_stdio(false);
	cin>>n>>m;
	m++;
	for(ri i=1;i<=n;i++){
		cin>>k>>s[i];
		node cur={i,s[i]};
		T[k].push_back(cur);
	}
	dfs(0,n+1);
	cout<<dp[0][m];
	return 0;
}

3.P1272 重建道路

這道題不難,只是要注意容量至少是 \(2\) ,分配的容量至少為 \(1\)

注意輸出需要迴圈找。

dp部分:

int dfs(int x){
	dp[x][1]=out[x];
	int sum=1;
	for(ri i=0;i<T[x].size();i++){
		int y=T[x][i];
		int tmp=dfs(y);
		sum+=tmp;
		for(ri j=min(sum,p);j>=2;j--){
			for(ri k=1;k<=min(j-1,tmp);k++){
				dp[x][j]=min(dp[x][j],dp[y][k]+dp[x][j-k]-1);
			}
		}
	}
	return sum;
}

輸出部分:

ans=dp[1][p];
for(ri i=2;i<=n;i++)ans=min(ans,dp[i][p]+1);
cout<<ans;

4.P1273 有線電視網

這道題需要一個鋪墊:P1510 精衛填海

可以吸取精衛填海的思路,就會做這題了(真的真的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define ll long long
#define ri register int
using namespace std;
const int MAXN=3010;
int n,m,k,a,c,pay[MAXN],dp[MAXN][MAXN];
struct node{
	int pos,cos;
};
vector <node> T[MAXN];

int dfs(int x){
	dp[x][0]=0;
	if(x>n-m){
		dp[x][1]=pay[x];
		return 1;
	}
	int sum=0;
	for(ri i=0;i<T[x].size();i++){
		int y=T[x][i].pos;
		int tmp=dfs(y);
		sum+=tmp;
		for(ri j=sum;j>=0;j--){
			for(ri k=0;k<=tmp;k++){
				dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[y][k]-T[x][i].cos);
			}
		}
	}
	return sum;
}

int main() {
	ios::sync_with_stdio(false);
	memset(dp,0xcf,sizeof(dp));
	cin>>n>>m;
	for(ri i=1;i<=n-m;i++){
		cin>>k;
		for(ri j=1;j<=k;j++){
			cin>>a>>c;
			node cur={a,c};
			T[i].push_back(cur);
		}
	}
	for(ri i=n-m+1;i<=n;i++)cin>>pay[i];
	dfs(1);
	for(ri i=m;i>=0;i--){
		if(dp[1][i]>=0){
			cout<<i;
			return 0;
		}
	}
	cout<<0;
	return 0;
}

差不多得了.jpg