代码如下:
#include <iostream>
#include <stdio.h>
using namespace std;
int n,k;//n个边,k个独立服务机构
typedef struct Node
{
//w记录总权值包括子结点,d距离服务机构(根节点)的距离,wd记录总运费,parent为父节点,
//lchild,rchild为孩子结点,wx记录该结点的权值,dx记录该结点的边长
int w,d,parent,lchild,rchild,wx,wd,dx;
int cost[100]; //记录费用
}BiNode; //定义一个结构体
BiNode sub[100];
void findc(int b,int i) //关联结点的函数
{
sub[i].parent=b; //i的父结点为b
if(sub[b].lchild==-1) sub[b].lchild=i; //如果b没有左子树,则令b为他的左子树
else sub[b].rchild=i; //如果b有左子树,则令b为他的右子树
}
void PreOrder(int t) //先序遍历,找出总距离
{
if(t!=-1) //若i为存在的结点下标
{
if(sub[t].parent!=-1) //若i存在父节点
{
sub[t].d+=sub[sub[t].parent].d;//距离加上父节点离服务区的距离
}
PreOrder(sub[t].lchild); //遍历出叶子结点离服务区的距离
PreOrder(sub[t].rchild); //遍历出叶子结点离服务区的距离
}
}
void PostOrder(int t) //后序遍历找出总运费和总权值
{
if(t!=-1) //若i为存在的结点下标
{
PostOrder(sub[t].lchild); //先遍历出叶子结点的运费
PostOrder(sub[t].rchild); //先遍历出叶子结点的运费
sub[t].wd=sub[t].w*sub[t].d; //求总运费
if(sub[t].lchild!=-1) //若i存在左子节点
{
sub[t].w+=sub[sub[t].lchild].w; //加上左结点的权值
sub[t].wd+=sub[sub[t].lchild].wd;//加上左结点的运费
}
if(sub[t].rchild!=-1) //若i存在右子节点
{
sub[t].w+=sub[sub[t].rchild].w;//加上右结点的权值
sub[t].wd+=sub[sub[t].rchild].wd;//加上右结点的运费
}
}
}
void Comp(int t) //动态规划,找出最小转移费用
{
if(sub[t].lchild==-1) //该结点为叶子结点
{
sub[t].cost[0]=sub[t].wd; //转移费用
for(int i=1;i<=k;i++) //同一个地方建立1-k个服务站
sub[t].cost[i]=0; //设立服务站的地方花费设为0
}
else //如果不为叶子结点
{
for(int i=0;i<=k;i++) //建立1-k个服务站
{
sub[t].cost[i]=sub[t].wd-sub[t].w*sub[t].d;//记录运费
for(int j=0;j<=i;j++) //设立0-i个服务站
{
int tmp = sub[sub[t].lchild].cost[j]+sub[t].wx*sub[t].d;//距离与权相乘为运费,加上左结点的运费
if(sub[t].lchild!=-1) tmp+=sub[sub[t].rchild].cost[i-j];//若有右结点,加上右结点
if(sub[t].cost[i]>tmp) sub[t].cost[i]=tmp; //找到比tmp大的,然后用tmp替换掉
}
}
}
}
void PostOrderComp(int t) //后序遍历
{
if(t!=-1)
{
PostOrderComp(sub[t].lchild); //先找齐叶子节点
PostOrderComp(sub[t].rchild); //先找齐叶子节点
Comp(t); //动态规划,找出各个结点最小转移费用
}
}
int main()
{
FILE *fp=fopen("输入.txt","r"); //文件输入数据
fscanf(fp,"%d %d",&n,&k); //文件输入边数和独立服务机构个数
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++) //初始化
sub[i].cost[j]=0;
sub[i].dx=sub[i].d=sub[i].w=sub[i].wx=sub[i].wd=0; //初始化
sub[i].parent=sub[i].lchild=sub[i].rchild=-1; //全部初始化为-1
}
int a,b,c;
for(int i=1;i<=n;i++) //输入数据
{
fscanf(fp,"%d %d %d",&a,&b,&c); //输入
sub[i].wx=sub[i].w=a; //记录权值
sub[i].dx=sub[i].d=c; //长度
findc(b,i); //关联结点,找到该结点父结点
}
PreOrder(0); //先序遍历,找出距离根节点距离
PostOrder(0); //后序遍历, 找出总运费和权值
PostOrderComp(0); //后序遍历设立服务站
printf("%d\n",sub[0].cost[k]); //输出最小转移费用,即根节点的花费cost[k]值
}
输入格式
4 2
1 0 1
1 1 10
10 2 5
1 2 3
输出
12
设计分析:
结构体数组BiNode sub[100]来存储各个结点,用parent,rchild,lchild记录父子结点的下标,关联起来。若为-1,则没有关联的结点。
本质上是利用了动态规划的方法,有些地方结合到了遍历的方法。函数PostOrderComp,PostOrder函数为后序遍历,PreOrder函数为先序遍历,Comp函数则为动态规划。
先用PreOrder函数先序遍历找出距离根节点距。
再用PostOrder函数后序遍历找出总运费和权值。
最后用PostOrderComp函数后序遍历并且调用里面的Comp函数比较出在各处设立独立服务机构的运费,来找出最小转移费用。
由于输入的数据较长,所以用文件输入,但是输出只有一个数据,所以直接黑框输出