/* sap的非递归实现 0ms 还不错 让人纠结的啊 我写的是吧d[]初始化为0 WA 看别人的模版初始化为m,过了 又看到别人用数组标记,过了(貌似也不对,居然过了) 我写了一个数组标记访问,没过 */ #include<stdio.h> #include<string.h> #include<queue> using namespace std; const int N=300; const int inf=0x7fffffff; int map[N][N]; int n,m,src,sink,minf; int d[N],numbs[N],curnode[N],pre[N],vis[N]; void revbfs()//初始化距离 { /* int u,v;//我的初始化为0 没过 谁给看看 memset(d,0,sizeof(d)); memset(numbs,0,sizeof(numbs)); d[sink]=0; numbs[0]=1; queue<int>q; q.push(sink); while(!q.empty()) { u=q.front(); q.pop(); for(v=1;v<=m;++v) { if(map[v][u]==0||d[v]||v==sink) continue; d[v]=d[u]+1; numbs[d[v]]++; q.push(v); } } return;*/ /* int i,u; //别人的数组标记访问 过了 queue<int>q; memset(vis,0,sizeof(vis)); d[sink]=0; vis[sink]=true; q.push(sink); while(!q.empty()) { u=q.front(); q.pop(); for(i=1; i<=m;++i) if(!vis[i] &&map[u][i]) { d[i]=d[u]+1; vis[i]=true; q.push(i); } }*/ /* int u,v;//我的素族标记访问 没过 memset(numbs,0,sizeof(numbs)); memset(vis,0,sizeof(vis)); d[sink]=0; numbs[0]=1; vis[sink]=1; queue<int>q; q.push(sink); while(!q.empty()) { u=q.front(); q.pop(); for(v=1;v<=m;++v) { if(!vis[v]&&map[v][u]) { vis[v]=1; d[v]=d[u]+1; numbs[d[v]]++; q.push(v); } } } return;*/ /* int u,v;//模板上的 初始化为m 过了 for(u=1;u<=m;++u) d[u]=m; memset(numbs,0,sizeof(numbs)); d[sink]=0; numbs[0]=1; queue<int>q; q.push(sink); while(!q.empty()) { u=q.front(); q.pop(); for(v=1;v<=m;++v) { if(map[v][u]==0||d[v]<m) continue; d[v]=d[u]+1; numbs[d[v]]++; q.push(v); } }*/ } int retreat(int &i)//修改d[i]=min(d[j]+1|) 更新相关numbs { int t,mind=m-1,j;//为什么mind=m-1?这就是最大的 for(j=1;j<=m;++j) if(map[i][j]>0&&d[j]<mind) mind=d[j]; t=d[i]; d[i]=mind+1; numbs[t]--; numbs[d[i]]++; if(i!=src) i=pre[i];//为什么 退回上一层 i中不到允许边了,这个操作试图找到允许边 没有这部分可能会造成死循环 return numbs[t]; } int augment()//找到最窄的路 然后按那个增广 { int i,j; int t=inf; for(i=sink,j=pre[i];i!=src;i=j,j=pre[i]) { if(t>map[j][i]) t=map[j][i]; } for(i=sink,j=pre[i];i!=src;i=j,j=pre[i]) { map[j][i]-=t; map[i][j]+=t; } return t; } int sap() { memset(pre,0,sizeof(pre)); int i,j; memset(d,0,sizeof(d));//放在这儿 可省去revbfs(); revbfs(); for(i=1;i<=m;++i) curnode[i]=1;//递归通过这个实现 curnode[i]表示的是i的后继结点的开始位置,小于它的已经访问过了 i=src; while(d[src]<m) { for(j=curnode[i];j<=m;++j) if(map[i][j]>0&&d[i]==d[j]+1) break; if(j<=m) { curnode[i]=j; pre[j]=i; i=j; if(i==sink) { minf+=augment(); i=src;//这都回去了 怎么实现递归? 通过cuenode实现 curnode是当前路径上的点 所以可以通过这个恢复递归状态 } }else { curnode[i]=1;//因为i没有可用的允许边,下面试图通过修改d[i]来寻找新的,若找到,那么新的都还没有搜索过,所以要从1开始 if(retreat(i)==0) break; } } return minf; } int main() { int i,a,b,c; while(scanf("%d%d",&n,&m)!=EOF) { minf=0; src=1; sink=m; memset(map,0,sizeof(map)); for(i=1;i<=n;++i) { scanf("%d%d%d",&a,&b,&c); map[a][b]+=c; } printf("%d\n",sap()); } }
/* 居然用了16ms 用循环写的时候,不用bfs好像也是0ms 这个跟那个好像差不多吧 居然运行了两次都是16ms 谁知道为什么啊 */ #include<stdio.h> #include<string.h> int m,n,src,sink; int d[205],num[205]; int map[205][205]; int min(int a,int b){return a<b?a:b;} int dfs(int u,int f) { if(u==sink) return f; int v,mind=n-1,last=f,cost;; for(v=1;v<=n;++v) { if(map[u][v]>0)//有流量 { if(d[u]==d[v]+1)//有允许边 { cost=dfs(v,min(last,map[u][v])); map[u][v]-=cost;//修改图 map[v][u]+=cost; last-=cost;//修改剩余流量 if(d[src]>=n)//结束标志 return f-last; if(last==0)//使用完就退出 break; } if(d[v]<mind)//下面更新距离的时候用 求其能连接到的最小距离 这个判断和上边的那个判断并列 因为他们是互斥的,只有当找不到允许边的时候才用得着mind mind=d[v]; } } if(last==f)//流量没有使用 即 没有允许边 { --num[d[u]]; if(num[d[u]]==0)//若出现断层 { d[src]=n; } d[u]=mind+1; ++num[d[u]]; } return f-last; } int sap() { int ret=0; memset(d,0,sizeof(d));//距离 memset(num,0,sizeof(num));//距离数 num[0]=n; while(d[src]<n) ret+=dfs(src,0x7fffffff); return ret;//最大流 } int main() { int i,a,b,c; while(scanf("%d%d",&m,&n)!=EOF) { src=1; sink=n; memset(map,0,sizeof(map)); for(i=1;i<=m;++i) { scanf("%d%d%d",&a,&b,&c); map[a][b]+=c; } printf("%d\n",sap()); } return 0; }