这题建图很巧妙。
首先要读懂题意:
m(m<=10)个内存区域,n(n<=50)个程序。找出一个方案来,使得平均结束时刻尽量小。题目保证有解。
同一个程序运行在不同大小的内存区域内,其运行时间不同。(注意,这里说的大小是指整个内存区域大小,而不是说:该程序之前有程序运行,占用了一部分内存,剩下的那部分内存大小。。。。。。。)
输入:m和n,然后是m个数表示内存区域大小。再是n行,每行第1个数为情况总数k(k<=10),然后是k对整数s1,t1,s2,t2……sk,tk,满足si<si+1表示在各个内存中的运行时间。
如果内存块总大小s不足s1,则无法在该内存块运行该程序;当si<=s<si+1时,运行时间为ti;当s>=sk时,运行时间为tk
输出:平均最小结束时间和调度方案。
分析:
对于在一个内存中的情况:设该内存中有K个程序。其运行时间分别为t1,t2……tk,则第i个程序结束时间Ti=t1+t2+……+ti,所有程序运行时间之和为kt1+(k-1)t2+(k-2)t3+……+tk。即对于在内存区域j中倒数第p个执行的程序i来说,其对于总的运行时间的贡献为p*Tij,Tij为第i个程序在第j个内存区域内运行的时间。
建图:
构造二分图G,X节点为n个程序,Y节点为m*n个位置:(j,p)表示第j个内存区域的倒数第p个程序。每个X节点与Y节点连一条权为p*Tij的边。
并不是每个匹配都对应一个合法方案,但最佳匹配一定对应一个合法方案,(把倒数第一个程序放到倒数第二个程序处,显然运行时间将减少。因此最佳方案不可能只有倒数第1个程序,而没有倒数第二个程序)
为什么要考虑倒数的程序以此建图,不能顺序考虑么?请戳这里
对于输出的问题:
建图后跑一次KM算法,得到的可行顶标之和就是最小的运行时间,除以n便得到平均值。(如果使用的KM算法是求最大权值匹配的话,建图的时候采用负权,求出的结果符号取反即可)
关键是方案如何输出?
由于建图的时候是以n个程序作为X节点(二分图左侧),运行KM算法时,Left保存的是位置(j,p)对应的程序编号。
于是,可以依次找每个内存区域j中第1个程序(p最大的且Left[j,p]不为0),得到该位置对应的程序编号,用一个数组记录该程序所在位置j。其起始时间为0,运行时间就是边的权值/p。用一个数组time来记录每一个内存区域已经分配的时间,便可得到下一个程序的起始时间。
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f struct P{ int k,s[11],t[11]; }p[55]; struct A{ int id,l,r; }Ans[55]; int mem[11],w[55][55*11],Left[55*11],Lx[55],Ly[11*55],slack[11*55],n,m; bool S[55],T[55*11]; bool match(int u){ S[u]=1; for(int v=1;v<=n*m;++v) if(!T[v]){ if(Lx[u]+Ly[v]-w[u][v]==0){ T[v]=1; if(!Left[v]||match(Left[v])){ Left[v]=u; return 1; } }else slack[v]=min(slack[v],Lx[u]+Ly[v]-w[u][v]); } return 0; } void KM(){ for(int i=1;i<=n;++i){ Lx[i]=-inf; for(int j=1;j<=n*m;++j) Lx[i]=max(Lx[i],w[i][j]); } for(int i=1;i<=n*m;++i) Left[i]=Ly[i]=0; for(int i=1;i<=n;++i){ for(int j=1;j<=n*m;++j) slack[j]=inf; for(;;){ for(int j=1;j<=n*m;++j) T[j]=0; for(int j=1;j<=n;++j) S[j]=0; if(match(i)) break; int a=inf; for(int j=1;j<=n*m;++j) if(!T[j]) a=min(a,slack[j]); for(int j=1;j<=n;++j) if(S[j]) Lx[j]-=a; for(int j=1;j<=n*m;++j) if(T[j]) Ly[j]+=a; } } } int ca=1; void output(){ if(ca!=1) puts(""); printf("Case %d\n",ca++); int ans=0; for(int i=1;i<=n;++i) ans+=Lx[i]; for(int j=1;j<=n*m;++j) ans+=Ly[j]; printf("Average turnaround time = %.2lf\n",n?-1.0*ans/n:0); int time[11]; memset(time,0,sizeof(time)); for(int i=1;i<=m;++i){ int p; for(p=1;p<=n;++p) if(!Left[(i-1)*n+p]) break; for(p=p-1;p>=1;--p){ int x=Left[(i-1)*n+p]; Ans[x].id=i; Ans[x].l=time[i]; Ans[x].r=time[i]-w[x][(i-1)*n+p]/p; time[i]-=w[x][(i-1)*n+p]/p; } } for(int i=1;i<=n;++i) printf("Program %d runs in region %d from %d to %d\n",i,Ans[i].id,Ans[i].l,Ans[i].r); } int main(){ int i,j,k; while(~scanf("%d%d",&m,&n)&&m&&n){ for(i=1;i<=m;++i) scanf("%d",&mem[i]); for(i=1;i<=n;++i){ scanf("%d",&p[i].k); for(j=1;j<=p[i].k;++j) scanf("%d%d",&p[i].s[j],&p[i].t[j]); } for(i=1;i<=n;++i) for(j=1;j<=m;++j){ int tmp; if(mem[j]<p[i].s[1]) tmp=inf; else{ int pos; for(pos=1;pos<=p[i].k;++pos)//找到第i个程序在第j个内存块内运行时间 if(p[i].s[pos]>mem[j]) break; --pos; tmp=p[i].t[pos]; } for(k=1;k<=n;++k){ if(tmp==inf) w[i][(j-1)*n+k]=-inf; else w[i][(j-1)*n+k]=-k*tmp; } } KM(); output(); } return 0; }