【Atcoder MSPC】E M's Solution
阿新 • • 發佈:2020-07-27
題目
給出二維平面內的\(n\)個帶權節點,初始時\(x\)軸和\(y\)軸已標記
你可以新增\(k\)個平行於座標軸的標記軸
一個點的代價定義為該點到最近的標記軸的距離和該點權值的乘積
對於$k = 0 \to n $ ,依次輸出最小代價和
$1 \le n \le 15 \ , \ -10^4 \le x_i ,y_i \le 10^4 ,\ 0\le p_i \le 10^6 $
題解
標記直線至少過一個已知點(我不會證)
這樣合理的直線只有\(2n\)條
注意到對於一個點,不會被兩條標記直線經過
因此可以\(O(3^n \times n^2 )\) 加剪枝
也可以考慮將\(2n\)條可能標記直線分配給\(n\)
記\(dp[S][k]\)為已經分配的集合為\(S\),分配的個數為\(k\)的最小代價
預處理\(cost[S]\)表示和選取一條標記線的集合\(S\)最小代價和
列舉子集轉移即可
複雜度\(O(n^22^n+n3^n)\)
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=15,M=1<<15; const ll inf=1e12; int n,ax[N],ay[N],b[N]; ll dp[M][N+1],cost[M]; int main(){ freopen("E.in","r",stdin); freopen("E.out","w",stdout); scanf("%d",&n); for(int i=0;i<n;++i)scanf("%d%d%d",&ax[i],&ay[i],&b[i]); for(int i=1;i<1<<n;++i){ ll sum;cost[i]=inf; sum=0; for(int j=0;j<n;++j)if(i&(1<<j)) sum+=1ll*b[j]*abs(ax[j]); cost[i]=min(cost[i],sum); sum=0; for(int j=0;j<n;++j)if(i&(1<<j)) sum+=1ll*b[j]*abs(ay[j]); cost[i]=min(cost[i],sum); } for(int i=1;i<1<<n;++i){ ll &tmp=dp[i][0]; tmp=inf; for(int t=i;t;t=(t-1)&i) tmp=min(tmp,cost[i^t]+cost[t]); } for(int i=0;i<1<<n;++i){ ll sum=0; for(int j=0;j<n;++j){ sum=0; for(int k=0;k<n;++k)if(i&(1<<k)) sum+=1ll*b[k]*abs(ax[j]-ax[k]); cost[i]=min(cost[i],sum); } for(int j=0;j<n;++j){ sum=0; for(int k=0;k<n;++k)if(i&(1<<k)) sum+=1ll*b[k]*abs(ay[j]-ay[k]); cost[i]=min(cost[i],sum); } } for(int i=0;i<1<<n;++i) for(int j=1;j<=n;++j){ ll &tmp=dp[i][j]; tmp=inf; for(int t=i;t;t=(t-1)&i) tmp=min(tmp,dp[i^t][j-1]+cost[t]); } for(int i=0;i<=n;++i)cout<<dp[(1<<n)-1][i]<<endl; return 0; }//tkys_Austin