bzoj 1070 [SCOI2007]修车——网络流(拆边)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1070
后面还有几辆车在这个人这儿修,自己这辆车的时间对总时间的贡献就要多乘上几倍。
所以可以费用流。每辆车向每个人连 n 条边,费用依次为 d , 2*d , 3*d …… 表示自己后面有几辆车。
对于一个人来说,还要限制走 k*d 这条边的一共只能有一辆车。只需要将一个人拆成 n 个点,每个点向汇点连容量为1的边就行了。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} const int N=605,M=66005,INF=65; int n,m,t,hd[N],xnt=1,cur[N],to[M],nxt[M],cap[M],w[M]; int dis[N],pre[N],info[N],ans; bool ins[N]; queue<int> q; void add(int x,int y,int z) { to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=1;w[xnt]=z; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-z; } bool spfa() { memset(dis,0x3f,sizeof dis);dis[0]=0; info[0]=INF;info[t]=0; q.push(0);ins[0]=1; while(q.size()) { int k=q.front();q.pop();ins[k]=0; for(int i=hd[k],v;i;i=nxt[i]) if(cap[i]&&dis[v=to[i]]>dis[k]+w[i]) { dis[v]=dis[k]+w[i]; pre[v]=i; info[v]=Mn(info[k],cap[i]); if(!ins[v])q.push(v),ins[v]=1; } } return info[t]; } void ek() { int s=info[t]; for(int i=pre[t];i;i=pre[to[i^1]]) { ans+=s*w[i];cap[i]-=s;cap[i^1]+=s; } } int main() { scanf("%d%d",&m,&n);int ct=n*m; t=ct+n+1; for(int i=1,x=ct+1,d;i<=n;i++,x++) for(int j=1,y=1;j<=m;j++,y++) { d=rdn(); for(int k=1,lj=d;k<=n;k++,lj+=d,y++) add(x,y,lj); } for(int i=1,x=ct+1;i<=n;i++,x++)add(0,x,0); for(int i=1;i<=ct;i++)add(i,t,0); while(spfa())ek(); printf("%.2f\n",(double)ans/n); return 0; }