【Atcoder MSPC】E M's Solution
题目
给出二维平面内的\(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