标签:style blog color os io for 问题 div log
每一户人家水的来源有两种打井和从别家接水,每户人家都可能向外输送水。
打井和接水两种的付出代价都接边。设一个超级源点,每家每户打井的代价就是从该点(0)到该户人家(1~n)的边的权值。接水有两种可能,从高处接水,那么代价是哈密顿距离与Y的乘积(可以认为就是水管的费用);从低处接水,还要加上付出水泵的钱(水管+水泵的费用)。这样就可以建图了。图论,会建图的话问题就解决一半了。
然后,用模版来解题。不过朱刘算法的模版时间复杂度的差异还是蛮大的。我的模版的建图是邻接矩阵,时间复杂度是O(N^3)。超时,过不了。在网上找了一份按边的起点、终点、权值建图的模版。AC了。该算法的复杂度是O(M)。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 1010, M=1010000,INF=0x3f3f3f3f; int pre[N],id[N],in[N],vis[N]; int tot;//边数 struct node { int u,v,w; }e[M]; void adde(int i,int j,int k) { e[tot].u=i;e[tot].v=j;e[tot++].w=k; } int zhuliu(int root ,int vn) { int ans=0; int cnt; while(1) { for(int i=0;i<vn;i++) in[i]=INF,id[i]=-1,vis[i]=-1; for(int i=0;i<tot;i++) { if(in[e[i].v]>e[i].w && e[i].u!=e[i].v) { pre[e[i].v]=e[i].u; in[e[i].v]=e[i].w; } } in[root]=0; pre[root]=root; for(int i=0;i<vn;i++) { ans+=in[i]; if(in[i]==INF)//这一步可以看出是否存在这样一棵树形图,因此可以略去DFS return -1; } cnt=0; for(int i=0;i<vn;i++) { if(vis[i]==-1) { int t=i; while(vis[t]==-1) { vis[t]=i; t=pre[t]; } if(vis[t]!=i || t==root) continue; for(int j=pre[t];j!=t;j=pre[j]) id[j]=cnt; id[t]=cnt++; } } if(cnt==0) break; for(int i=0;i<vn;i++) if(id[i]==-1) id[i]=cnt++; for(int i=0;i<tot;i++) { int u,v; u=e[i].u; v=e[i].v; e[i].u=id[u]; e[i].v=id[v]; e[i].w-=in[v]; } vn=cnt; root=id[root]; } return ans; } struct Node { int a,b,c; }nd[N]; int main() { //freopen("test.txt","r",stdin); int n,x,y,z,i,j,k,d; while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF) { if(!n) break; tot=0; for(i=1;i<=n;i++) { scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c); adde(0,i,nd[i].c*x); } for(i=1;i<=n;i++) { scanf("%d",&k); while(k--) { scanf("%d",&j); if(i==j) continue; d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c); d*=y; if(nd[i].c<nd[j].c) d+=z; adde(i,j,d); } } int ans=zhuliu(0,n+1); if(ans==-1) printf("poor XiaoA\n"); else printf("%d\n",ans); } return 0; }
下面是邻接矩阵建图的模版,虽然超时了。但是模版还是有借鉴意义的。模版来自哈工大出版的《图论及应用》。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N = 1010, INF=0x3f3f3f3f; int Map[N][N]; bool vis[N],f[N]; //f是缩点标记,1表示该点已经被缩掉 int pre[N]; int zhuliu(int n) { int sum=0; int i,j,k; for(i=0;i<n;i++) { f[i]=0; Map[i][i]=INF; } pre[0]=0; while(1) { //求最短弧集合E0 for(i=1;i<n;i++) { if(f[i]) continue; pre[i]=i; for(j=0;j<n;j++) if(!f[j]&&Map[j][i]<Map[pre[i]][i]) pre[i]=j; if(pre[i]==i) return -1; //没有入边,不存在最小树形图 } //检查E0 for(i=1;i<n;i++) { if(f[i]) continue ; //从当前点开始找环 for(j=0;j<n;j++) vis[j]=0; vis[0]=1; j=i; do { vis[j]=1; j=pre[j]; } while(!vis[j]) ; if(!j) continue; //没有找到环 //收缩G中的有向环 i=j; //将整个环的权值保存,累计入原图的最小树形图 do { sum+=Map[pre[j]][j]; j=pre[j]; } while(j!=i) ; j=i; //对与环上的点有关的边,修改边权 do { for(k=0; k<n; k++) if(!f[k]&&Map[k][j]<INF&&k!=pre[j]) Map[k][j]-= Map[pre[j]][j]; j=pre[j]; } while(j!=i) ; //缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i for(j=0;j<n;j++) { if(j==i) continue; for(k=pre[i]; k!=i; k=pre[k]) { if(Map[k][j]<Map[i][j]) Map[i][j] =Map[k][j]; if(Map[j][k]<Map[j][i]) Map[j][i] =Map[j][k]; } } //标记环上其他的点为被缩掉 for(j=pre[i];j!=i;j=pre[j]) f[j] =1; //当前环缩点结束,形成新的图G’,跳出继续求G’的最小树形图 break; } //如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是 //最小树形图,累计如sum, 算法结束 if(i==n) { for(i=1;i<n;i++) if(!f[i]) sum+=Map[pre[i]][i]; break; } } return sum; } struct node { int a,b,c; }nd[N]; int main() { //freopen("test.txt","r",stdin); int n,x,y,z,i,j,k,d; while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF) { if(!n) break; for(i=0;i<=n;i++) for(j=0;j<i;j++) Map[j][i]=Map[i][j]=INF; for(i=1;i<=n;i++) { scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c); Map[0][i]=nd[i].c*x; } for(i=1;i<=n;i++) { scanf("%d",&k); while(k--) { scanf("%d",&j); if(i==j) continue; d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c); d*=y; if(nd[i].c>=nd[j].c) d+=z; Map[i][j]=min(d,Map[i][j]); } } printf("%d\n",zhuliu(n+1)); } return 0; }
标签:style blog color os io for 问题 div log
原文地址:http://www.cnblogs.com/Potato-lover/p/3942321.html