4 4 2 1 1 0 1 2 0 0 1 3 0 0 2 4 1 -1 3 4 3 -1 4 4 2 1 1 0 1 2 0 0 1 3 3 1 2 4 1 -1 3 4 3 -1
4 0 4 3
题意:
题意:给出一张N(N<=100)个点,M(M<=1000条)边的有向图。每个点上都有一些人。每条边有4个属性(u,v,w,p)。这些边分为三种:(1)p<0时,表示这条边是隧道,这条隧道从u连向v,虽然如果想通过这条隧道的话没有流量限制,但可以最多只容纳w人;(2)p=0时,这条边是道路,由u连向v,通过没有流量限制;(3)p>0时,表示这条边是古老的桥,u连向v,如果不修这座桥,则只能通过1人,但是如果花费w的费用修桥的话,则通过这座桥的流量便没有限制。桥的总数<12。求使得最多的人能够躲到隧道里时候的人数和在该情况下的最小费用。
解题:
先建源点S=0与每个有人的点建一条边,容量为人数。如果p<0表示的是隧道,需建两条边:( u , v , INF ) , ( u , T , w)。如果p==0则建:( u ,v , INF).
如果p>0则表示可能需要修的桥,记下每个桥在图的id号与修桥的花费,并建一条初始状态的边:( u ,v , 1)。建完图后,开始枚举修桥的状态,并修改对应边的容量,求最大流,求完一次,还原修改过的边容量为1。
#include<stdio.h> #include<string.h> #include<queue> using namespace std; #define captype i const int MAXN = 100010; //点的总数 const int MAXM = 400010; //边的总数 const int INF = 1<<30; struct EDG{ int to,next; captype cap,flow; } edg[MAXM]; int eid,head[MAXN]; int gap[MAXN]; //每种距离(或可认为是高度)点的个数 int dis[MAXN]; //每个点到终点eNode 的最短距离 int cur[MAXN]; //cur[u] 表示从u点出发可流经 cur[u] 号边 void init(){ eid=0; memset(head,-1,sizeof(head)); } //有向边 三个参数,无向边4个参数 void addEdg(int u,int v,captype c,captype rc=0){ edg[eid].to=v; edg[eid].next=head[u]; edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++; edg[eid].to=u; edg[eid].next=head[v]; edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++; } //预处理eNode点到所有点的最短距离 void BFS(int sNode, int eNode){ queue<int>q; memset(gap,0,sizeof(gap)); memset(dis,-1,sizeof(dis)); gap[0]=1; dis[eNode]=0; q.push(eNode); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u]; i!=-1; i=edg[i].next){ int v=edg[i].to; if(dis[v]==-1){ dis[v]=dis[u]+1; gap[dis[v]]++; q.push(v); } } } } int S[MAXN]; //路径栈,存的是边的id号 captype maxFlow_sap(int sNode,int eNode, int n){ //注意:n为点的总个数,包括源点与汇点 BFS(sNode, eNode); //预处理eNode到所有点的最短距离 if(dis[sNode]==-1) return 0; //源点到不可到达汇点 memcpy(cur,head,sizeof(head)); int top=0; //栈顶 captype ans=0; //最大流 int u=sNode; while(dis[sNode]<n){ //判断从sNode点有没有流向下一个相邻的点 if(u==eNode){ //找到一条可增流的路 captype Min=INF ; int inser; for(int i=0; i<top; i++) //从这条可增流的路找到最多可增的流量Min if(Min>edg[S[i]].cap-edg[S[i]].flow){ Min=edg[S[i]].cap-edg[S[i]].flow; inser=i; } for(int i=0; i<top; i++){ edg[S[i]].flow+=Min; edg[S[i]^1].flow-=Min; //可回流的边的流量 } ans+=Min; top=inser; //从这条可增流的路中的流量瓶颈 边的上一条边那里是可以再增流的,所以只从断流量瓶颈 边裁断 u=edg[S[top]^1].to; //流量瓶颈 边的起始点 continue; } bool flag = false; //判断能否从u点出发可往相邻点流 int v; for(int i=cur[u]; i!=-1; i=edg[i].next){ v=edg[i].to; if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){ flag=true; cur[u]=i; break; } } if(flag){ S[top++] = cur[u]; //加入一条边 u=v; continue; } //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1 int Mind= n; for(int i=head[u]; i!=-1; i=edg[i].next) if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){ Mind=dis[edg[i].to]; cur[u]=i; } gap[dis[u]]--; if(gap[dis[u]]==0) return ans; //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径 //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流 dis[u]=Mind+1; //如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1 gap[dis[u]]++; if(u!=sNode) u=edg[S[--top]^1].to; //退一条边 } return ans; } struct Bridge{ int eid,cost; }fixEdg[15]; int main(){ int n,m ,u,v,w,p; while(scanf("%d%d",&n,&m)>0){ init(); int s=0, t=n+1; for(int i=1;i<=n;i++){ scanf("%d",&w); if(w) addEdg(s,i,w); //源点与有人的点连接 } int k=0; while(m--){ scanf("%d%d%d%d",&u,&v,&w,&p); if(p<0){ addEdg(u,v,INF); addEdg(u,t,w); } else if(p==0){ addEdg(u,v,INF); } else{ fixEdg[k].eid=eid; fixEdg[k++].cost=w; addEdg(u,v,1); } } int maxNum=0,minCost=0; for(int sta=0; sta<(1<<k); sta++){ int cost=0; for(int i=0; (1<<i)<=sta; i++) if(sta&(1<<i)){ cost+=fixEdg[i].cost; edg[fixEdg[i].eid].cap=INF; } for(int i=0; i<eid; i++) edg[i].flow=0; int ans = maxFlow_sap(s,t,t+1); if(ans>maxNum){ maxNum=ans; minCost=cost; } else if(ans==maxNum&&cost<minCost) minCost=cost; for(int i=0; (1<<i)<=sta; i++) if(sta&(1<<i)){ edg[fixEdg[i].eid].cap=1; } } if(maxNum==0) printf("Poor Heaven Empire\n"); else printf("%d %d\n",maxNum,minCost); } }
HDU 4309 Seikimatsu Occult Tonneru(最大流SAP+状态压缩枚举)
原文地址:http://blog.csdn.net/u010372095/article/details/46581639