问题描述
栋栋居住在一个繁华的C市中,然而,这个城市的道路大都年久失修。市长准备重新修一些路以方便市民,于是找到了栋栋,希望栋栋能帮助他。
C市中有n个比较重要的地点,市长希望这些地点重点被考虑。现在可以修一些道路来连接其中的一些地点,每条道路可以连接其中的两个地点。另外由于C市有一条河从中穿过,也可以在其中的一些地点建设码头,所有建了码头的地点可以通过河道连接。
栋栋拿到了允许建设的道路的信息,包括每条可以建设的道路的花费,以及哪些地点可以建设码头和建设码头的花费。
输入格式
输入的第一行包含两个整数n, m,分别表示C市中重要地点的个数和可以建设的道路条数。所有地点从1到n依次编号。
接下来m行,每行三个整数a, b, c,表示可以建设一条从地点a到地点b的道路,花费为c。若c为正,表示建设是花钱的,如果c为负,则表示建设了道路后还可以赚钱(比如建设收费道路)。
接下来一行,包含n个整数w_1, w_2, …, w_n。如果w_i为正数,则表示在地点i建设码头的花费,如果w_i为-1,则表示地点i无法建设码头。
输入保证至少存在一个方法使得任意两个地点能只通过新修的路或者河道互达。
样例输入
5 5
1 2 4
1 3 -1
2 3 3
2 4 5
4 5 10
-1 10 10 1 1
样例输出
9
样例说明
建设第2、3、4条道路,在地点4、5建设码头,总的花费为9。
思路:
1:先更新两个城市之间的权值(在道路与河道之间选择最小的值)
2:采用prim生成最小生成树->>同时判断加入的边是不是河道(是则最后要减去多次使用码头的费用,因为码头建一次就够了)
#include<stdio.h>
#define MAXVEX 200 //最大顶点个数
#define INF 32768
typedef int vextype;
typedef struct{
int arcs[MAXVEX][MAXVEX];//边的信息
int arcsflag[MAXVEX][MAXVEX];//(1->河道0->道路)
int arcnum; //边的数目
vextype vex[MAXVEX][2]; //顶点信息:码头花费值
int vexnum; //顶点的数目
}AdjMatrix;//邻接矩阵
typedef struct{
vextype start,end;//边的顶点值
int arcs;//顶点
int flag;//河道顶点的使用次数
}prim;//0号不用
AdjMatrix G;
void Create(AdjMatrix *G){
int i,j,weight,vex1,vex2;
char ch;
scanf("%d %d",&G->vexnum,&G->arcnum);
for(i=1 ; i<=G->vexnum ; i++){ //初始化无向网
for(j=1 ; j<=G->vexnum ; j++){
G->arcs[i][j] = INF;
}
}
for(i=1 ; i<=G->vexnum ; ++i){ //顶点信息
G->vex[i][0] = i;
}
for(i=1 ; i<=G->arcnum ; ++i){
scanf("%d %d",&vex1,&vex2);
scanf("%d",&weight);
G->arcs[f(vex1)][f(vex2)] = weight;
G->arcs[f(vex2)][f(vex1)] = weight;
}
for(i=1 ; i<=G->vexnum ; i++){
scanf("%d",&G->vex[i][1]);
}
}
int main(void){
int i,j,k,m,min;
int sum = 0;
int start = 1;
prim closedge[MAXVEX];
Create(&G);
//替换最小代价
for(i=1 ; i<=G.vexnum ; i++){
for(j=i+1 ; j<=G.vexnum ; j++){
if(G.vex[i][1] != -1 && G.vex[j][1] != -1){
k = G.vex[i][1]+G.vex[j][1];
if(G.arcs[i][j] > k){
G.arcs[i][j] = k;
G.arcs[j][i] = k;
G.arcsflag[i][j] = 1; //使用河道的标记
G.arcsflag[j][i] = 1;
}
}
}
}
closedge[f(start)].start = start; //边开始点
closedge[f(start)].end = start; //边结束点就是顶点的信息
closedge[f(start)].arcs = 0; //边的值
closedge[f(start)].flag = -1; //初始使用码头的次数为-1
//初始化数组
for(i=1 ; i<=G.vexnum ; i++){
if(i != f(start)){
closedge[i].start = G.vex[start][0]; //边开始点
closedge[i].end = G.vex[i][0]; //边结束点就是顶点的信息
closedge[i].arcs = G.arcs[f(start)][i]; //边的值
closedge[i].flag = -1; //初始次数为-1
}
}
for(i=1 ; i<G.vexnum ; i++){ //选n-1条边
//选最小的边
min = INF;
for(j=1 ; j<=G.vexnum ; j++){
if(closedge[j].arcs && closedge[j].arcs < min){
min = closedge[j].arcs;
m = j;
}
}
//使用码头的点加1
if(G.arcsflag[f(closedge[m].start)][f(closedge[m].end)]){
closedge[f(closedge[m].start)].flag++;
closedge[f(closedge[m].end)].flag++;
}
sum += min; //最小花费
closedge[m].arcs = 0; //找到一个顶点
//更新数组
for(j=1 ; j<=G.vexnum ; j++){
if(G.arcs[m][j]<closedge[j].arcs && closedge[j].arcs){
closedge[j].start = m;
closedge[j].arcs = G.arcs[m][j];
}
}
}
//减去多余的码头钱
for(i=1 ; i<=G.vexnum ; i++){
if(closedge[i].flag > 0){
sum -= G.vex[i][1]*closedge[i].flag;
}
}
printf("%d",sum);
return 0;
}