这题建图很巧妙。
首先要读懂题意:
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;
}