[SCOI2007]修车
OJ题号:
BZOJ1070、洛谷2053
思路:
最初的爆零解法:
每个工人向$S$连一条容量为$inf$、费用为$0$的边,(每个工人可以修很多车)
每个工人向每辆车连一条容量为$1$,费用等于题目描述的边,(一个工人只能修一次同一辆车)
每辆车向$T$连一条容量为$1$,费用为$0$的边。(每辆车总共只能修一次)
每次增广以后将$S$到该工人之间的费用增加修理该车所用的时间,然后不停重复。
正确解法:
把每个工人拆成$n$个点,向每辆车连一条边,边权依次增加,表示该工人的第$i$个点表示第$i$次修理车所花的费用。
其余边与我错误的解法相同。
最后跑一遍最小费用最大流即可。
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 #include<cstring> 6 inline int getint() { 7 char ch; 8 while(!isdigit(ch=getchar())); 9 int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int inf=0x7fffffff; 14 int s,t; 15 struct Edge { 16 int from,to,remain,cost; 17 }; 18 const int E=100000,V=800; 19 Edge e[E]; 20 std::vector<int> g[V]; 21 int sz=0; 22 inline void add_edge(const int u,const int v,const int w,const int c) { 23 e[sz]=(Edge){u,v,w,c}; 24 g[u].push_back(sz); 25 sz++; 26 } 27 int p[V],d[V],a[V]; 28 bool inq[V]; 29 inline void Augment() { 30 memset(a,0,sizeof a); 31 a[s]=inf; 32 std::queue<int> q; 33 q.push(s); 34 memset(inq,0,sizeof inq); 35 inq[s]=true; 36 d[s]=0; 37 for(int i=1;i<=t;i++) d[i]=inf; 38 while(!q.empty()) { 39 int x=q.front(); 40 q.pop(); 41 inq[x]=false; 42 for(unsigned i=0;i<g[x].size();i++) { 43 Edge &y=e[g[x][i]]; 44 if((d[x]+y.cost<d[y.to])&&y.remain) { 45 p[y.to]=g[x][i]; 46 d[y.to]=d[x]+y.cost; 47 a[y.to]=std::min(a[x],y.remain); 48 if(!inq[y.to]) { 49 q.push(y.to); 50 inq[y.to]=true; 51 } 52 } 53 } 54 } 55 } 56 int m,n; 57 inline double EdmondsKarp() { 58 int maxflow=0,mincost=0; 59 while(maxflow!=n) { 60 Augment(); 61 for(int i=t;i!=s;i=e[p[i]].from) { 62 e[p[i]].remain-=a[t]; 63 e[p[i]^1].remain+=a[t]; 64 } 65 mincost+=d[t]; 66 maxflow++; 67 } 68 return mincost; 69 } 70 int main() { 71 m=getint(),n=getint(); 72 s=0,t=m*n+n+1; 73 for(int i=1;i<=m*n;i++) { 74 add_edge(s,i,1,0); 75 add_edge(i,s,0,0); 76 } 77 for(int i=1;i<=n;i++) { 78 for(int j=1;j<=m;j++) { 79 int cost=getint(); 80 for(int k=1;k<=n;k++) { 81 add_edge((j-1)*n+k,m*n+i,1,cost*k); 82 add_edge(m*n+i,(j-1)*n+k,0,-cost*k); 83 } 84 } 85 } 86 for(int i=1;i<=n;i++) { 87 add_edge(m*n+i,t,1,0); 88 add_edge(t,m*n+i,0,0); 89 } 90 printf("%.2f\n",(double)EdmondsKarp()/n); 91 return 0; 92 }