洛谷P3177树上染色
作者:互联网
题意:
给n,K,n个点的树,K个点是黑色的,n-k点是白色的
给n-1条u,v,w,收益值是黑点两两距离之和,和白点两两距离之和的总值
求最大的收益值
思路:
因为是树形的,所以想到了求dp,dp[u][i] 求u为根的选有多少i个黑点的收益值,然后卡壳
翻了题解,发现dp[u][i] 求得是以u为根的 i 个黑点的贡献值,就是有一个通式:这条边左边的黑点*这条边右边的黑点*w+这条边左边的白色*这条边右边的白点*w
突然就豁然开朗了,仔细想想,我应该……应该个p 笨脑子不可能想到算贡献的,告辞。这个通式如果是自己想到推出来,可能会吹一辈子
接下来就是在树里面进行dp了
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+val);
val就是那个通式:
这条边左边黑点数:k
这条边右边黑点数:K-k
这条边左边白点数:sz[v]-k,sz[v]是以u为根连接着 v 点的子树点数
这条边右边的白点数:n-K-(sz[v]-k)
接下来就是类似01背包操作吧
写得过程各种wa,tle,参考了别人ac代码,修修改改终于ac了
#include<bits/stdc++.h> using namespace std; #define ll long long #define il inline #define it register int #define inf 0x3f3f3f3f #define lowbit(x) (x)&(-x) #define mem(a,b) memset(a,b,sizeof(a)) #define mod 1000000007 const int maxn=2010; struct node{ int to,next; ll w; }a[maxn<<1]; int n,k1,cnt,tot,head[maxn]; ll f[maxn][maxn]; int sz[maxn]={0}; il void add(int u,int v,ll w){ a[tot].w=w;a[tot].next=head[u]; a[tot].to=v;head[u]=tot++; } void dfs(int u,int qian){ sz[u]=1;f[u][0]=f[u][1]=0; for(it i=head[u];i!=-1;i=a[i].next){ int v=a[i].to; if(v==qian){continue;} dfs(v,u); sz[u]+=sz[v]; } for(it i=head[u];i!=-1;i=a[i].next){ int v=a[i].to; if(v==qian){continue;} for(it j=min(sz[u],k1);j>=0;j--){ for(it k=0;k<=min(j,sz[v]);k++){ if(f[u][j-k]>=0){ ll val=(ll)k*(k1-k)*a[i].w+(ll)(sz[v]-k)*(n-k1-sz[v]+k)*a[i].w; //cout<<val<<" "<<j<<" "<<k<<endl; f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+val); } } } } } int main(){ scanf("%d%d",&n,&k1); tot=0;mem(head,-1);mem(f,-inf); for(it i=1;i<n;i++){ int u,v; ll w; scanf("%d%d%lld",&u,&v,&w); add(u,v,w);add(v,u,w); } dfs(1,-1); //cout<<sz[1]<<endl; printf("%lld\n",f[1][k1]); return 0; } /* 4 1 1 2 1 1 3 1 1 4 1 6 */View Code
……希望在这条路上多坚持坚持
标签:sz,洛谷,黑点,染色,ll,P3177,这条,dp,define 来源: https://www.cnblogs.com/luoyugongxi/p/12267113.html