传送门
//题意: 现在有些城市着火了, 需要建立一定数量的消防站, 每一个点建站有一个不同的花费, 有边权, 如果一个城市没有建立消防站 , 那么至少离他一个limit的距离比较有个消防站, 不同的点limit也不同, 问满足条件的最小花费是多少?
//思路: 我们并不能通过一次遍历而得到我们需要的答案. 我们可以考虑暴力判断每一个点是否可以作为当前点的依附点(即在这个点建立消防站), 那么我们就有一个n^2的算法, 综合数据发现, 这个n最大也只是1000, 所以n^2是完全够的, 为了实现这个求解我们需要一些辅助数组.
best[x] 代表的是x的子树下满足题意的最小花费是多少. 显然最后答案就是best[1].
dp[u]][j] 代表的是u依附于j时u的子树内满足条件的最小花费是多少. (j就是我们暴力判断的每一个点)
转移方程就是:
best[x] = min(dp[x][j],best[x]);
dp[u][j] = sum( min(dp[v][j] - cost[j], best[v]) );
那么就可以写啦
AC Code
const int maxn = 1e3+5;
int cas=1;
int cnt ,head[maxn];
int dis[maxn][maxn];
int cost[maxn],limit[maxn];
int best[maxn],dp[maxn][maxn];
struct node
{
int to,w,next;
}e[maxn<<1];
void add(int u,int v,int w)
{
e[cnt] = (node){v,w,head[u]};
head[u] = cnt++;
}
int root,n;
void dfs_dis(int u,int fa,int len)//预处理出所有点对之间的距离.
{
dis[root][u] = len;
for(int i=head[u]; ~i ; i = e[i].next){
int to = e[i].to;
if(to == fa) continue;
dfs_dis(to,u,len+e[i].w);
}
}
void dfs(int u,int fa)
{
for(int i=head[u]; ~i ; i=e[i].next){
int to = e[i].to;
if(to == fa) continue;
dfs(to,u); //递归到叶子结点在进行操作.
}
for(int i=1;i<=n;i++){ //暴力枚举每一个点寻找最优解.
if(dis[i][u]>limit[u]) continue; //距离不符合
dp[u][i] = cost[i];
for(int j=head[u]; ~j ; j = e[j].next){
int to = e[j].to;
if(to == fa) continue;
dp[u][i] += min(dp[to][i] - cost[i],best[to]);
//如果v,i没有依赖关系,则dp[v][i] = inf;
}
best[u] = min(dp[u][i],best[u]);
}
}
void solve()
{
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++) scanf("%d",&cost[i]);
for(int i=1;i<=n;i++) scanf("%d",&limit[i]);
cnt = 0; Fill(head,-1);
for(int i=1;iint u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w); add(v,u,w);
}
Fill(dis,inf);
for(int i=1;i<=n;i++) dfs_dis(root = i,-1,0);
Fill(dp,inf); Fill(best,inf);
dfs(1,-1);
cout << best[1] << endl;
}
}