题意:给定你一颗树,要求你在这棵树中确定K个黑点和N-K个白点,使黑点间与白点间两两距离之和最大,输出最大值.n<=2000
对于这道题,我想了好几个思路,包括点分治,贪心,动规,网络流等等,实在无法,敲了个贪心,但最后结果出来后题解是树形动规.
动规尤其树形动规我也做过不少,这道题给了我很大启发.
梳理一下我最初的否决的思路:设f[i][j]表示向i子树加入j个黑点所得到的最大收益,但我发现这样搞一是不好转移,二是后效性没有解除,三是复杂度惊人,就放弃了.
得到正解之后,又仔细想了一下,有一个很明显的东西是,一个子树内部无论结构如何,只要确定它的黑点白点个数,对外面的影响就是确定的,这就是后效性的解释.
转移可以视为背包,至于复杂度,复杂度ydc有专门的解释.
这题就没什么了.
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<cmath> #include<algorithm> #include<queue> #include<set> #include<map> #include<iomanip> using namespace std; #define LL long long #define up(i,j,n) for(int i=j;i<=n;i++) #define pii pair<int,int> #define db double #define eps 1e-4 #define FILE "dealing" int read(){ int x=0,f=1,ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0',ch=getchar();} return x*f; } const LL maxn=2200,inf=10000000000000LL; bool cmin(int& a,int b){return a>b?a=b,true:false;} //bool cmax(int& a,int b){return a<b?a,b,true:false;} bool cmin(LL& a,LL b){return a>b?a=b,true:false;} int n,K; struct node{ int y,next,v; }e[maxn<<1]; int linkk[maxn],len=0; void insert(int x,int y,int v){e[++len].v=v;e[len].next=linkk[x];linkk[x]=len;e[len].y=y;} LL f[maxn][maxn],siz[maxn],fa[maxn]; void dfs(int x){ siz[x]=1; f[x][0]=f[x][1]=0; for(int i=linkk[x];i;i=e[i].next){ if(e[i].y==fa[x])continue; fa[e[i].y]=x; dfs(e[i].y); siz[x]+=siz[e[i].y]; for(int j=siz[x];j>=0;j--){ for(int k=0;k<=siz[e[i].y]&&k<=j;k++){ LL ans=k*(K-k)+(siz[e[i].y]-k)*(n-K-(siz[e[i].y]-k)); ans*=e[i].v; ans+=f[e[i].y][k]; f[x][j]=max((LL)f[x][j],f[x][j-k]+ans); } } } } int main(){ freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); n=read(),K=read(); memset(f,-10,sizeof(f)); up(i,1,n-1){ int x=read(),y=read(),v=read(); insert(x,y,v);insert(y,x,v); } dfs(1); cout<<f[1][K]<<endl; return 0; }