4033: [HAOI2015]树上染色
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 3269 Solved: 1413
[Submit][Status][Discuss]
Description
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并
将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。
问收益最大值是多少。
Input
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。
输入保证所有点之间是联通的。
N<=2000,0<=K<=N
Output
输出一个正整数,表示收益的最大值。
Sample Input
5 2
1 2 3
1 5 1
2 3 1
2 4 2
Sample Output
17
【样例解释】
将点1,2染黑就能获得最大收益。
HINT
2017.9.12新加数据一组 By GXZlegend
Source
#include<iostream> #include<cstdio> #include<cstring> #define N 2001 using namespace std; int n,m,k,ans,cnt; int dis[N][N],e[N][N]; int p[N]; void calc_dis() { for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) if(i!=j && j!=k) e[i][j]=min(e[i][j],e[i][k]+e[k][j]); } } void calc_ans() { int tmp=0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(p[i]==p[j]) tmp+=e[i][j]; ans=max(ans,tmp); } void dfs(int now,int c) { if(now==n+1) { if(c==k)calc_ans(); return; } p[now]=0;dfs(now+1,c+1); p[now]=1;dfs(now+1,c); } int main() { int x,y,z; scanf("%d%d",&n,&k); memset(e,127/3,sizeof e); for(int i=1;i<=n;i++) e[i][i]=0; for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); e[x][y]=e[y][x]=z; } calc_dis(); dfs(1,0); printf("%d\n",ans); return 0; }
30暴力
/* 开始状态设的是f[i][j][0/1]。 表示i为根的子树染j个黑点,i节点为黑/白的最大值。 发现当i与儿子异色时没法转移,需要知道子树内点的具体染色情况。 所以说这个状态貌似不大行。 网上说“像这种题目其实是一个套路。在树上这种“两两之间”计算贡献和的问题,都拆开看每条边的贡献。” 我就很伤心,那不就说明我被套路了吗... 改一下状态f[i][j]表示i为根染j个黑点对总答案的最大贡献。 这个贡献的定义是啥呢?就是子树内所有边的贡献。 对于这条边 子树下有j个黑点,那么在其他位置就有k - j个黑点,所以由于黑点这条边被走过j*(k-j)次。 子树下有size[x] - j个白点,其他位置有n - k - (size[x]-j)个白点 所以由于白点这条边被走过(size[x]-j)*(n-k-size[x]+j)次。 */ #include<iostream> #include<cstdio> #include<cstring> #define N 2001 #define ll long long using namespace std; int n,k,ans,cnt,S,T; int head[N],siz[N]; ll f[N][N],tmp; struct edge{ int u,v,net; ll w; }e[N<<1]; inline int read() { int x=0,f=1;char c=getchar(); while(c>‘9‘||c<‘0‘){if(c==‘-‘)f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();} return x*f; } inline void add(int u,int v,ll w) { e[++cnt].v=v;e[cnt].w=w;e[cnt].net=head[u];head[u]=cnt; } ll calc(ll val,int num,int x)//计算这条边的贡献 { val=val*x*(k-x)+val*(num-x)*(n-k-(num-x)); return val; } void dfs(int u) { siz[u]=1; for(int i=head[u];i;i=e[i].net) { int v=e[i].v; if(siz[v]) continue; dfs(v); for(int x=siz[u];x>=0;x--) for(int y=siz[v];y>=0;y--)//类似树上背包,枚举当前子树染黑个数 { tmp=f[u][x]+f[v][y]+calc(e[i].w,siz[v],y); //f[u][x]其他子树内的点各自独立于当前子树内的点的贡献 //f[v][y]当前子树内的点各自独立于其他子树内的点的贡献 f[u][x+y]=max(f[u][x+y],tmp); } siz[u]+=siz[v]; } } int main() { int x,y,z; n=read();k=read(); for(int i=1;i<n;i++) { x=read();y=read();cin>>z; add(x,y,z);add(y,x,z); } dfs(1);printf("%lld\n",f[1][k]); return 0; }
原文地址:https://www.cnblogs.com/L-Memory/p/9768069.html
时间: 2024-10-10 03:45:14