The 2018 ACM-ICPC CCPC寧夏 G-Factories(樹形dp+揹包)
阿新 • • 發佈:2019-01-30
題目:給你n個城市,n-1條道路,每兩個城市僅有一條通路,即一個樹形結構。讓你選擇m個葉子節點建立工廠,使得最終任意兩個工廠之間距離的累加和最小。
思路:考慮點之間的關係很繁瑣,所以我想的是對於一條邊來說考慮經過了它多少次。dp[u][i]表示u節點為根的子樹上選擇了i個葉子節點,會經過u這個子樹的邊的權值和的最優值。轉移方程如下:
dp[u][i]=min( dp[u][i-j]+dp[v][j]+w*j*(m-j) ); v是u的子節點,w是u與v之間邊的權值,k*(m-k)表示的是這條邊會被經過的次數(j為從v這個樹上過來的葉節點數,m-j為其它地方選擇的葉節點數)
我特麼是智障啊,思路早就想對了,怎麼考慮都沒錯,一直wa,最後才發現結果需要輸出 " Case #%d: %lld\n " !!!!!!傻逼錯誤一直犯,還要注意freopen()!
#include <bits/stdc++.h> using namespace std; #define ll long long #define inf 1e12 const int maxn=1e5+10; struct node{ int x; ll w; node(int _x,ll _w) { x=_x; w=_w; } }; vector<node>a[maxn]; int n,t,m,siz[maxn]; ll dp[maxn][102]; void dfs(int u,int fa) { if(a[u].size()==1) { dp[u][1]=0; siz[u]=1;///記錄葉節點數量 } dp[u][0]=0; for(int i=0;i<a[u].size();i++) { int v=a[u][i].x; ll w=a[u][i].w; if(v==fa) continue; dfs(v,u); siz[u]+=siz[v]; dp[u][m]=min(dp[v][m],dp[u][m]);///直接從v上選了m個葉子 for(int j=min(m,siz[u]);j>=1;j--) { for(int k=1;k<=min(j,siz[v]);k++) dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]+1ll*w*1ll*k*1ll*(m-k));//轉移方程 } } } int main() { ///freopen("in.txt","r",stdin); scanf("%d",&t); int cas=0; while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) a[i].clear(); for(int i=1;i<n;i++) { int x,y; ll w; scanf("%d%d%lld",&x,&y,&w); a[x].push_back(node(y,w)); a[y].push_back(node(x,w)); } memset(siz,0,sizeof siz); int rt=1; for(int i=1;i<=n;i++)//找到一個不為葉子的節點作為根 if(a[i].size()>1) { rt=i; break; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) dp[i][j]=inf; for(int i=1;i<=n;i++) dp[i][0]=0; dfs(rt,-1); printf("Case #%d: %lld\n",++cas,dp[rt][m]); } return 0; }