题目背景
班级聚会的时候,班主任为了方便管理,规定吃饭的时候同一个寝室的同学必须坐在一起;但是吃完饭后,到了娱乐时间,喜欢不同游戏的同学会聚到一起;在这个过程中就涉及到了座位分配的问题。
题目描述
有 j 桌中选,可以是这些桌中的任何一个座位。确定好新座位之后,大家开始移动,移动的体力消耗按照如下规则计算:
移动座位过程分为两步:
- 从起始桌移动到目标桌对应座位,这个过程中的体力消耗为两桌距离的两倍,即从第 |;
2.从目标桌的对应座位绕着桌子移动到目标座位,由于桌子是圆的,所以客人会选择最近的方向移动,体力消耗为移动距离的一倍,即从编号为 );
详情如下图:
现在,给定每个客人的限制(即每个人的新座位所在的区间),需要你设计一个方案,使得所有客人消耗的体力和最小;本题中假设客人在移动的时候互不影响。
输入格式
从标准输入读入数据。
第一行输入两个数 m;
接下来输入 j;
接下来输入 j。
输出格式
输出到标准输出。
输出总体力消耗的最小值,如果没有合法的方案输出no solution。
-
- 由原来的人向现在的每个可达的位置连边桌子连边做费用流($zkw$)有70;
- 设源$S$,汇$T$,原来的人为$s_{ij}$,现在的位置为$t_{ij}$,同桌之间移动的代价$c1$,不同桌的为$c2$;
- 这题的一大特点就是原来的人向现在的对应位置的连边是区间$[L_{i,j},R_{i,j}]$;
- 考虑用线段树优化建边,就是所有的$s_{i,j}$点先连向线段树,再连向$t_{i,j}$;
- 由于要共用线段树的节点所以需要对代价$c2$做一些转化;
- 将$t$拆成$t1$和$t2$分别表示不同桌向左走或向右走,$S$连向$s_{i,j}$代价$i*2$ ,$t1$,$t2$连向$T$代价$-i*2$;
- 建两颗线段树,叶子节点分别连向$t1$和$t2$,区间[L,R]拆成[L,i]和[i,R]即可向线段树节点连边;
- 节点个数$O(3*nm + 8*nm)$,边数$O( 3*nm + 4*nm + 8*nm + logn * nm)$
-
70pts
1 #include<bits/stdc++.h> 2 #define inf 0x3f3f3f3f 3 using namespace std; 4 const int N=1000010,K=310; 5 int S,T,n,m,o,hd[N],dis[N],vis[N],sum1[K],sum2[K]; 6 int id1[K][K],id2[K][K],id3[K][K],id4[K][K],L[K][K],R[K][K]; 7 struct Edge{int v,nt,f,w;}E[N<<1]; 8 void adde(int u,int v,int f,int w){ 9 E[o]=(Edge){v,hd[u],f,w};hd[u]=o++; 10 E[o]=(Edge){u,hd[v],0,-w};hd[v]=o++; 11 } 12 queue<int>q; 13 bool bfs(){ 14 memset(vis,0,sizeof(vis)); 15 memset(dis,0x3f,sizeof(dis)); 16 q.push(T);dis[T]=0;vis[T]=1; 17 while(!q.empty()){ 18 int u=q.front();q.pop(); 19 vis[u]=0; 20 for(int i=hd[u];~i;i=E[i].nt)if(E[i^1].f){ 21 int v=E[i].v; 22 if(dis[v]>dis[u]+E[i^1].w){ 23 dis[v]=dis[u]+E[i^1].w; 24 if(!vis[v])vis[v]=1,q.push(v); 25 } 26 } 27 } 28 return dis[S]!=inf; 29 } 30 int dfs(int u,int F){ 31 vis[u]=1; 32 if(u==T||!F)return F; 33 int flow=0,f; 34 for(int i=hd[u];~i;i=E[i].nt){ 35 int v=E[i].v; 36 if(!vis[v]&&dis[v]+E[i].w==dis[u]&&E[i].f&&(f=dfs(v,min(F,E[i].f)))){ 37 flow+=f;F-=f; 38 E[i].f-=f,E[i^1].f+=f; 39 if(!F)break; 40 } 41 } 42 return flow; 43 } 44 int zkw(){ 45 int flow=0,cost=0,f; 46 while(bfs()){ 47 do{ 48 memset(vis,0,sizeof(vis)); 49 f=dfs(S,inf); 50 flow+=f; 51 cost+=dis[S]*f; 52 }while(vis[T]); 53 } 54 return flow==n*m?cost:-1; 55 } 56 int main(){ 57 // freopen("C.in","r",stdin); 58 // freopen("C.out","w",stdout); 59 memset(hd,-1,sizeof(hd)); 60 scanf("%d%d",&n,&m); 61 for(int i=1;i<=n;i++) 62 for(int j=1;j<=m;j++){ 63 id1[i][j]=(i-1)*m+j; 64 id2[i][j]=n*m+id1[i][j]; 65 id3[i][j]=n*m+id2[i][j]; 66 id4[i][j]=n*m+id3[i][j]; 67 } 68 S=0,T=id4[n][m]+1; 69 for(int i=1;i<=n;i++){ 70 for(int j=1;j<=m;j++){ 71 adde(S,id1[i][j],1,0); 72 adde(id2[i][j],id4[i][j],1,0); 73 adde(id3[i][j],id4[i][j],1,0); 74 adde(id4[i][j],T,1,0); 75 int ri=j%m+1,le=(j+m-2)%m+1; 76 adde(id2[i][j],id2[i][ri],inf,1); 77 adde(id2[i][j],id2[i][le],inf,1); 78 adde(id3[i][j],id3[i][ri],inf,1); 79 adde(id3[i][j],id3[i][le],inf,1); 80 if(i>1)adde(id2[i][j],id2[i-1][j],inf,2); 81 if(i<n)adde(id3[i][j],id3[i+1][j],inf,2); 82 } 83 } 84 for(int i=1;i<=n;i++) 85 for(int j=1;j<=m;j++){scanf("%d",&L[i][j]);sum2[++L[i][j]]++;} 86 for(int i=1;i<=n;i++) 87 for(int j=1;j<=m;j++){scanf("%d",&R[i][j]);sum1[++R[i][j]]++;} 88 for(int i=1;i<=n;i++){ 89 sum1[i]+=sum1[i-1]; 90 if(sum1[i]>i*m)return puts("no solution"),0; 91 } 92 for(int i=n;i>=1;i--){ 93 sum2[i]+=sum2[i+1]; 94 if(sum2[i]>(n-i+1)*m)return puts("no solution"),0; 95 } 96 for(int i=1;i<=n;i++) 97 for(int j=1;j<=m;j++){ 98 if(L[i][j]<=i&&i<=R[i][j])adde(id1[i][j],id2[i][j],1,0),adde(id1[i][j],id3[i][j],1,0); 99 else if(i<L[i][j])adde(id1[i][j],id3[L[i][j]][j],1,(L[i][j]-i)<<1); 100 else adde(id1[i][j],id2[R[i][j]][j],1,(i-R[i][j])<<1); 101 } 102 int ans = zkw(); 103 if(!~ans)puts("-1"); 104 else printf("%d\n",ans); 105 return 0; 106 }