dp练习(4.16)

P2014 [CTSC1997]选课

转化一下题意,给定一棵树, 求包含 0 0 0的结点一棵子树的点权和最大。

多叉树问题:

1.转二叉树。

2.树形背包。

int dfs(int u){
	int sz=1;
	for(int i=h[u];~i;i=e[i].nt){
		int v=e[i].to;
		int tmp=dfs(v);
		sz+=tmp;
		for(int j=sz;j>1;j--)	
			for(int k=0;k<=tmp;k++)
				if(j-k>0) dp[u][j]=max(dp[u][j],dp[v][k]+dp[u][j-k]);
	}
	return sz;
}
void solve(){
	//think twice code once
	scanf("%d%d",&n,&m);
	mst(h,-1);
	for(int i=1,x,y;i<=n;i++){
		scanf("%d%d",&x,&y);
		dp[i][1]=y;
		add(x,i);
	}
	dfs(0);
	printf("%d\n",dp[0][m+1]);
}

P3478 [POI2008]STA-Station

比较裸的转移题,先从1出发预处理除1的答案和sz[]数组。

然后进行转移

d p [ v ] = d p [ u ] + ( n − 2 s z [ v ] ) dp[v]=dp[u]+(n-2sz[v]) dp[v]=dp[u]+(n2sz[v])

取个最值即可。

code

void dfs(int u,int fa,int d){
	dp[1]+=d;sz[u]=1;
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u,d+1);
		sz[u]+=sz[v];
	}
}
void dfs(int u,int fa){
	if(ans<dp[u]) ans=dp[u],id=u;
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to;
		if(v==fa) continue;
		dp[v]=dp[u]+(n-2*sz[v]);
		dfs(v,u);
	}
}

P3052 [USACO12MAR]Cows in a Skyscraper G

状压好题,需要维护一个sz[]数组,表示当前状态对应的最后一个组剩下的空间。

时间复杂度: O ( n 2 n ) O(n2^n) O(n2n)

void solve(){
	scanf("%d%d",&n,&w);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	mst(f,0x3f);
	f[0]=1,sz[0]=w;
	int st=1<<n;
	for(int i=0;i<st;i++)	
		for(int j=0;j<n;j++)
			if(!(i>>j&1)){
				if(sz[i]>=a[j]){
					if(f[i|1<<j]>=f[i])
		f[i|1<<j]=min(f[i|1<<j],f[i]),sz[i|1<<j]=max(sz[i|1<<j],sz[i]-a[j]);
				}
				else if(f[i|1<<j]>=f[i]+1){
		f[i|1<<j]=min(f[i|1<<j],f[i]+1),sz[i|1<<j]=max(sz[i|1<<j],w-a[j]);		
				}
			}
	printf("%d\n",f[st-1]);
	//think twice code once
}

P2679 [NOIP2015 提高组] 子串

需要维护一个前缀和,然后就是裸的dp,需要滚动数组优化

void solve(){
	scanf("%d%d%d",&n,&m,&k1);
	scanf("%s%s",a+1,b+1);
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=m;j;j--)
			for(int k=k1;k;k--){
				if(a[i]==b[j]){
					sum[j][k]=(sum[j-1][k]+dp[j-1][k-1])%mod;
					dp[j][k]=(dp[j][k]+sum[j][k])%mod;
				}
				else sum[j][k]=0;
			}
	printf("%d\n",dp[m][k1]%mod);
	//think twice code once
}