POJ 2146 Minimum Cost KM/最小费用流
http://poj.org/problem?id=2516
题意:N个批发商,M个供应商,K种商品,每个批发商每种商品都需要一定的数量,每个供应商每种商品都提供一定的数量。
每个供应商向每个批发商运送某种单位数量的商品
KM 思路:每种商品 需要a个,那x边就有a个点,该种商品供应有b个,那y边就有b个点,
权值为,运费。。。说的有点乱。就是我看了别人的这种思路之后,输入还是没搞定。。。这输入也挺有技巧的,我是
这样觉得 匈牙利算法时间复杂度O(ne) ,KM每找到一条增广路径要顶标调整,复杂度O(ne^2) 不知道我这个KM的复杂度
是不是那么小
代码(KM):
#include<iostream> #include<cstdio> #include<string> #include<cstring> #define inf 1<<30 #define Max(a,b)a>b?a:b #define Min(a,b)a<b?a:b #define nMAX 155 using namespace std; int map[nMAX][nMAX],link[nMAX],lx[nMAX],ly[nMAX]; bool vx[nMAX],vy[nMAX]; int n,m; bool dfs(int u) { int j; vx[u]=1; for(j=1;j<=m;j++) if(!vy[j]&&map[u][j]==lx[u]+ly[j])//!!!map[u][j]==lx[u]+ly[j] { vy[j]=1; if(!link[j]||dfs(link[j])) { link[j]=u; return true; } } return false; } int KM() { int i,j,k; for(i=1;i<=n;i++) { lx[i]=-inf; for(j=1;j<=m;j++) lx[i]=Max(lx[i],map[i][j]);//每个map[i][j]都已初始化 } memset(ly,0,sizeof(ly)); memset(link,0,sizeof(link)); for(i=1;i<=n;i++) { while(1) { memset(vx,0,sizeof(vx)); memset(vy,0,sizeof(vy)); if(dfs(i))break; //修改 lx ly int MIN=inf; for(j=1;j<=n;j++) if(vx[j]) for(k=1;k<=m;k++) if(!vy[k]) MIN=Min(MIN,lx[j]+ly[k]-map[j][k]); for(j=1;j<=n;j++)if(vx[j])lx[j]-=MIN;//lx[j]--;错错错 for(j=1;j<=m;j++)if(vy[j])ly[j]+=MIN; } } int ANS=0; for(i=1;i<=m;i++) if(link[i]) ANS+=map[link[i]][i]; return ANS; } int main() { int cn[55][nMAX],cm[55][nMAX],com1[55],com2[55],f[55][55]; int N,M,K; int i,j,h,cnt; while(scanf("%d%d%d",&N,&M,&K)) { if(N==0&&M==0&&K==0)break; memset(com1,0,sizeof(com1)); memset(com2,0,sizeof(com2)); // memset(cn,0,sizeof(cn)); // memset(cm,0,sizeof(cm)); for(i=1;i<=N;i++) for(j=1;j<=K;j++) { scanf("%d",&cnt); while(cnt--) cn[j][++com1[j]]=i; //第com1[j]件第j中商品,第i个shop需要 } for(i=1;i<=M;i++) for(j=1;j<=K;j++) { scanf("%d",&cnt); while(cnt--) cm[j][++com2[j]]=i; //同理 } bool flag=1; int ans=0; for(h=1;h<=K;h++) { for(i=1;i<=N;i++) for(j=1;j<=M;j++) scanf("%d",&f[i][j]); if(com1[h]>com2[h]) { flag=0; continue; } memset(map,0,sizeof(map)); for(i=1;i<=com1[h];i++) for(j=1;j<=com2[h];j++) { map[i][j]=-f[cn[h][i]][cm[h][j]]; } n=com1[h]; m=com2[h]; ans-=KM(); } if(!flag)printf("%d\n",-1); else printf("%d\n",ans); } return 0; }
最小费用最大流
思路:因为涉及到K种商品,所以 考虑到 N*K个需求者,M*K个供应者,就是一种需求者只需求一种产品,一种供应者只提供一种产品;需求第i 个产品的需求者 向提供第i的供应真连线,cap=inf,cost=运费,源点到需求者连线 cap=需求量,cost=0 ,供应者到汇点连线,cap=提供量,cost=0。。。就是基于把每个需求者分成K个需求者,把每个供应者分成K个供应者。。。结果TLE,囧 时间复杂度 最坏有2500+2500+2个点,(2500*50+2500*2)*2=26*10^4 条边 ,费用流的时间复杂度是O(2*M*C)吧?!好像是,m是边,C是流量3 , O(2*M)是spfa的复杂度, 最后用时 26*10^4*2*2500*3=3.9*10^9 怪不得超时
然后网上是单独的算出每种产品的费用,因为每种产品是相互独立的,然后加起来,那时间复杂度是多少呢?每一种产品 最坏情况用50+50+2个点,(50*50+50*2)*2=5200条边 O(2*m*c)=1.56*10^6 ,50种产品就有 50*1.56*10^6=7.8*10^7
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #define nMAX 110 #define mMAX 6000 #define inf 1<<26 using namespace std; int s,n; int head[nMAX],s_edge,ans,dis[nMAX],pre[nMAX],qu[nMAX],num; int need[55][55],provide[55][55],cntn[55],cntp[55]; bool vs[nMAX]; struct Edge { int u,v,cap,cost,nxt; }edge[mMAX]; void addedge(int u,int v,int cap,int cost) { s_edge++; edge[s_edge].v=v; edge[s_edge].cap=cap; edge[s_edge].cost=cost; edge[s_edge].nxt=head[u]; head[u]=s_edge; s_edge++; edge[s_edge].v=u; edge[s_edge].cap=0; edge[s_edge].cost=-cost; edge[s_edge].nxt=head[v]; head[v]=s_edge; } bool spfa() { int i,beg,tail; for(i=0;i<=n;i++) { dis[i]=inf; vs[i]=0; } beg=0,tail=1; dis[s]=0; vs[s]=1; qu[0]=s; while(beg!=tail) { int u=qu[beg++]; for(int e=head[u];e;e=edge[e].nxt) { int v=edge[e].v; if(edge[e].cap&&dis[v]-dis[u]>edge[e].cost) { dis[v]=dis[u]+edge[e].cost; pre[v]=e; if(!vs[v]) { vs[v]=1; qu[tail++]=v; if(tail==nMAX)tail=0; } } } vs[u]=0; if(beg==nMAX)beg=0; } if(dis[n]!=inf)return 1; return 0; } void end() { int u,p; int MIN=inf; for(u=n;u!=s;u=edge[p^1].v) { p=pre[u]; MIN=MIN<edge[p].cap?MIN:edge[p].cap; } num+=MIN; for(u=n;u!=s;u=edge[p^1].v) { p=pre[u]; edge[p].cap-=MIN; edge[p^1].cap+=MIN; ans+=(edge[p].cost*MIN); } } int main() { int N,M,K,i,j,f,w; while(~scanf("%d%d%d",&N,&M,&K)) { if(N==0&&M==0&&K==0)break; memset(cntn,0,sizeof(cntn)); memset(cntp,0,sizeof(cntp)); for(i=1;i<=N;i++) for(j=1;j<=K;j++) { scanf("%d",&need[i][j]); cntn[j]+=need[i][j]; } for(i=1;i<=M;i++) for(j=1;j<=K;j++) { scanf("%d",&provide[i][j]); cntp[j]+=provide[i][j]; } bool fg=1; ans=0; for(i=1;i<=K;i++) { s_edge=1; memset(head,0,sizeof(head)); s=0,n=N+M+1; for(j=1;j<=N;j++) for(f=1;f<=M;f++) { scanf("%d",&w); addedge(j,N+f,inf,w); } if(cntp[i]<cntn[i]){fg=0;continue;} for(j=1;j<=N;j++) addedge(s,j,need[j][i],0); for(j=1;j<=M;j++) addedge(N+j,n,provide[j][i],0); num=0; if(fg) while(spfa()) { end(); } if(num<cntn[i])fg=0;//不能break啊,数据读不完就WA!!!^ ^ } if(fg) printf("%d\n",ans); else printf("-1\n"); } return 0; }