题目链接:点击打开链接
2 10 20 30 1 3 2 2 4 1 1 2 2 1 2 0 0 0 0
30HintIn 3‐dimensional space Manhattan distance of point A (x1, y1, z1) and B(x2, y2, z2) is |x2‐x1|+|y2‐y1|+|z2‐z1|.
题意:n,X,Y,Z分别代表,n个村子,每个村子可以选择自己凿井或者从有水的村子里引水过来。如果选择自己凿井,那么所需要的费用就是自家高度(z坐标)乘以X
如果选择从别人家引水,那么费用是两个村子的欧几里得距离诚意Y,如果终点比起点要高那么还需要加上Z。 最后问你让所有人家里都有水,最少需要多少费用。
思路:创造一个超级源点0号点代表自家凿井,这道题其实不存在找不到路径的情况,因为最起码可以每个村子都自己凿井,也就是从0号店到每个村子各一条边,这样的话就是一个最小树形图的问题了。
这里用了网上大牛的模板。 具体的实现就不多说了。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define N 1010 #define INF 100000000 struct Point { int x,y,z; }p[N]; struct Edge { int u,v,w; }e[N*N]; int m,in[N],vis[N],pre[N],id[N]; void addedge(int u,int v,int w) { e[m].u=u; e[m].v=v; e[m++].w=w; } int dist(Point a,Point b) { return abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z); } int Directed_MST(int root,int NV,int NE) { int ret = 0; while(true){ ///步骤1:找到最小边 for(int i = 0;i < NV;i ++) in[i] = INF; for(int i = 0;i < NE;i ++){ int u = e[i].u , v = e[i].v; if(e[i].w < in[v] && u != v){ pre[v] = u; in[v] = e[i].w; } } for(int i = 0;i < NV;i ++){ if(i == root) continue; if(in[i] == INF) return -1;///除了根节点以外有点没有入边,则根无法到达他 } int cntnode = 0; memset(id,-1,sizeof(id)); memset(vis,-1,sizeof(vis)); ///找环 in[root] = 0; for(int i = 0;i < NV;i ++){///标记每个环,编号 ret += in[i]; int v = i; while(vis[v] != i && id[v] == -1 && v != root){ vis[v] = i; v = pre[v]; } if(v != root && id[v] == -1){ for(int u = pre[v];u != v;u = pre[u]){ id[u] = cntnode; } id[v] = cntnode ++; } } if(cntnode == 0) break;//无环 for(int i = 0;i < NV;i ++) if(id[i] == -1){ id[i] = cntnode ++; } ///步骤3:缩点,重新标记 for(int i = 0;i < NE;i ++){ int v = e[i].v; e[i].u = id[e[i].u]; e[i].v = id[e[i].v]; if(e[i].u != e[i].v) e[i].w -= in[v]; } NV = cntnode; root = id[root]; } return ret; } int main() { int n,X,Y,Z; int k,v; while(scanf("%d %d %d %d",&n,&X,&Y,&Z)&&n) { m=0; for(int i=1;i<=n;i++) { scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].z); addedge(0,i,p[i].z*X); } for(int i=1;i<=n;i++) { scanf("%d",&k); for(int j=1;j<=k;j++) { scanf("%d",&v); if(i==v) continue; int w=dist(p[i],p[v])*Y; if(p[v].z>p[i].z) w+=Z; addedge(i,v,w); } } int ans=Directed_MST(0,n+1,m); if(ans==-1) printf("poor XiaoA\n"); else printf("%d\n",ans); } return 0; }模板:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define N 1010///根据实际情况改变 #define INF 100000000 struct Edge { int u,v,w; }e[N*N]; int m,in[N],vis[N],pre[N],id[N]; void addedge(int u,int v,int w) { e[m].u=u; e[m].v=v; e[m++].w=w; } int Directed_MST(int root,int NV,int NE)///没给根的情况下要假设一个超级源点,到各边的距离sum是全部边的和+1. ///如果求出来的和等于-1或者大于等于2*sum就不满足,否则减去sum就是正解 { int ret = 0; while(true){ ///步骤1:找到最小边 for(int i = 0;i < NV;i ++) in[i] = INF; for(int i = 0;i < NE;i ++){ int u = e[i].u , v = e[i].v; if(e[i].w < in[v] && u != v){ pre[v] = u; in[v] = e[i].w; ///if(u==root) cntroot=i;cntroot-题目给的边的条数就是根的编号 } } for(int i = 0;i < NV;i ++){ if(i == root) continue; if(in[i] == INF) return -1;///除了根节点以外有点没有入边,则根无法到达他 } int cntnode = 0; memset(id,-1,sizeof(id)); memset(vis,-1,sizeof(vis)); ///找环 in[root] = 0; for(int i = 0;i < NV;i ++){///标记每个环,编号 ret += in[i]; int v = i; while(vis[v] != i && id[v] == -1 && v != root){ vis[v] = i; v = pre[v]; } if(v != root && id[v] == -1){ for(int u = pre[v];u != v;u = pre[u]){ id[u] = cntnode; } id[v] = cntnode ++; } } if(cntnode == 0) break;//无环 for(int i = 0;i < NV;i ++) if(id[i] == -1){ id[i] = cntnode ++; } ///步骤3:缩点,重新标记 for(int i = 0;i < NE;i ++){ int v = e[i].v; e[i].u = id[e[i].u]; e[i].v = id[e[i].v]; if(e[i].u != e[i].v) e[i].w -= in[v]; } NV = cntnode; root = id[root]; } return ret;///最小树形图的长度 }