bzoj4711-小奇挖矿
Posted ubospica
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4711-小奇挖矿相关的知识,希望对你有一定的参考价值。
Description
【题目背景】
小奇在喵星系使用了无限非概率驱动的采矿机,以至于在所有星球上都采出了一些矿石,现在它准备建一些矿石仓库并把矿石运到各个仓库里。
【问题描述】
喵星系有n个星球,标号为1到n,星球以及星球间的航线形成一棵树。所有星球间的双向航线的长度都为1。小奇要在若干个星球建矿石仓库,设立每个仓库的费用为K。对于未设立矿石仓库的星球,设其到一个仓库的距离为i,则将矿石运回的费用为Di。请你帮它决策最小化费用。
输入
第一行2个整数n,K。
第二行n-1个整数,D1,D2,…Dn-1,保证Di<=Di+1。
接下来n-1行,每行2个整数x,y,表示星球x和星球y存在双向航线。
n<=200,0<=K,Di<=100000
输出
输出一行一个整数,表示最小费用。
样例输入
8 10
2 5 9 11 15 19 20
1 4
1 3
1 7
4 6
2 8
2 3
3 5
样例输出
38
【样例解释】
在1,2号星球建立仓库。
Solution
似乎和std并不一样... 好像还更快?
下文中 "连向" 表示运向某个仓库.
容易发现一个点 \(p\) 的儿子 \(v\) 连向的点要么与 \(p\) 连向的点相同, 要么在 \(v\) 的子树内.
设 \(f_{i,j \in \left\{-n,n\right\} }\) 表示\(i\) 点连向距离 \(|j|\) 的点, \(i\) 子树内点的运输/建立仓库的总费用的最小值, 并且 \(i\)
\[
\begin{cases}
连向父亲 & (j>0) \自己为仓库 & (j=0) \连向某个儿子 & (j<0)
\end{cases}
\]
.
考虑转移: 令 \(d > 0\), \(v\) 为 \(i\) 的儿子,
\[
\begin{cases}
f_{i,-d} = d_d + \min_{v_0 \in \{v\}}\{ f_{v_0,-(d-1)} + \sum_{v \not= v0} (\min \{f_{v,d+1},f_{v,\le 0}\} )\} \f_{i,0} = K + \sum_{v} (\min \{f_{v,1},f_{v,\le 0}\}) \f_{i,d} = d_d + \sum_{v} (\min \{f_{v,d+1},f_{v,\le 0}\} )
\end{cases}
\]
边界条件挺复杂的... 看代码吧...
然后这个东西可以前缀min优化, 时间复杂度 \(\operatorname O (n^2)\)
愉快的拿了目前rk1... (虽然是在本校OJ评测的)
Code
#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;
//--------------------------------------
const int nsz=250,ninf=1e9;
int n,k,d[nsz];
vector<int> edge[nsz];
void adde(int f,int t){edge[f].push_back(t),edge[t].push_back(f);}
int dp[nsz][nsz*2],minv[nsz],maxd[nsz];
void dfs(int p,int f){
for(vector<int>::iterator i=edge[p].begin();i!=edge[p].end();){
int v=*i;
if(v!=f)dfs(v,p),maxd[p]=max(maxd[p],maxd[v]+1),++i;
else i=edge[p].erase(i);
}
rep(i,1,maxd[p]){
dp[p][-i+n]=d[i];
if(i==n-1){
dp[p][-i+n]+=dp[edge[p].front()][-(i-1)+n];
continue;
}
int a=0,b=ninf,tmp;
for(int v:edge[p]){
int tmp=min(dp[v][i+1+n],minv[v]);
a+=tmp;
if(maxd[v]>=i-1)b=min(b,dp[v][-(i-1)+n]-tmp);
}
dp[p][-i+n]+=a+b;
}
rep(i,0,n-1){
dp[p][i+n]=d[i];
if(i==n-1){
for(int v:edge[p])dp[p][i+n]+=minv[v];
continue;
}
for(int v:edge[p])dp[p][i+n]+=min(dp[v][i+1+n],minv[v]);
}
minv[p]=ninf;
rep(i,-maxd[p],0)minv[p]=min(minv[p],dp[p][i+n]);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>k;
rep(i,1,n-1)cin>>d[i];
d[0]=k;
int a,b;
rep(i,1,n-1)cin>>a>>b,adde(a,b);
dfs(1,0);
cout<<minv[1]<<'\n';
return 0;
}
以上是关于bzoj4711-小奇挖矿的主要内容,如果未能解决你的问题,请参考以下文章