【LA2238 训练指南】固定分区内存管理 【二分图最佳完美匹配,费用流】
题意
早期的多程序操作系统常把所有的可用内存划分为一些大小固定的区域,不同的区域一般大小不同,而所有区域的大小之和为可用内存的大小。给定一些程序,操作系统需要给每个程序分配一个区域,使得他们可以同时执行。可是每个程序的运行时间可能和它所占有的内存区域大小有关,因此调度并不容易。
编程计算最优的内存分配策略,既给定m个区域的大小和n个程序在各种内存环境下的运行时间,找出一个调度方案,使得平均结束时刻尽量小。具体来说,你需要给每个程序分配一个区域,使得没有两个程序同时在同一个时间运行在同一个内存区域中,而所有程序分配的区域大小都不小于该程序的最低内存需求。程序对内存的需求不会超过最大内存块的大小。
分析
这个题最重要的是明白一个很重要的结论:在同一个内存中,第倒数第k个执行的程序对于该内存总结束时间的贡献为kT,T为该程序在该内存下的运行时间。
有了这个结论,建图就并不难了。
下面我们来构造二分图,X结点为n个程序,Y结点为n*m个位置,其中位置(j,p)表示第j个内存的倒数第p个执行的程序。每个结点X和Y结点(j,p)之间连一条边权为pTi,j的边,然后求最小权匹配就可以。
并不是每个匹配都对应一个合法方案,但是,最佳匹配一定是对应一个合法方案。
我下面用费用流写的代码并不能AC,等以后看看是不是能找到bug。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 #include <queue> 6 7 using namespace std; 8 const int maxn=6000+10; 9 const int maxm=100000+10; 10 const int INF=1e9; 11 struct MCMF{ 12 int head[maxn],to[maxm],Next[maxm],from[maxm],flow[maxm],cap[maxm]; 13 long long cost[maxm]; 14 int n,m,s,t,sz; 15 int inq[maxn]; 16 int d[maxn]; 17 int p[maxn]; 18 int a[maxn]; 19 20 void init(int n){ 21 this->n=n; 22 sz=-1; 23 memset(head,-1,sizeof(head)); 24 } 25 void AddEdge(int a,int b,int ca,long long co){ 26 ++sz; 27 to[sz]=b,from[sz]=a,Next[sz]=head[a],head[a]=sz; 28 flow[sz]=0,cap[sz]=ca,cost[sz]=co; 29 ++sz; 30 to[sz]=a,from[sz]=b,Next[sz]=head[b],head[b]=sz; 31 flow[sz]=ca,cap[sz]=ca,cost[sz]=-co; 32 } 33 bool BellmanFord(int s,int t,int &Flow,long long &Cost){ 34 for(int i=0;i<=n;i++)d[i]=INF; 35 memset(inq,0,sizeof(inq)); 36 d[s]=0;inq[s]=1;p[s]=-1;a[s]=INF; 37 queue<int>Q; 38 Q.push(s); 39 while(!Q.empty()){ 40 int u=Q.front();Q.pop(); 41 inq[u]=0; 42 for(int i=head[u];i!=-1;i=Next[i]){ 43 int v=to[i]; 44 if(cap[i]>flow[i]&&d[v]>d[u]+cost[i]){ 45 d[v]=d[u]+cost[i]; 46 p[v]=i; 47 a[v]=min(a[u],cap[i]-flow[i]); 48 if(!inq[v]){ 49 Q.push(v); 50 inq[v]=1; 51 } 52 } 53 } 54 } 55 if(d[t]==INF)return false; 56 Flow+=a[t]; 57 Cost+=(long long)d[t]*(long long)a[t]; 58 int u=t; 59 60 while(u!=s){ 61 flow[p[u]]+=a[t]; 62 flow[p[u]^1]-=a[t]; 63 u=from[p[u]]; 64 } 65 return true; 66 } 67 68 long long Mincost(int s,int t){ 69 int Flow=0; 70 long long Cost=0; 71 while(BellmanFord(s,t,Flow,Cost)); 72 return Cost; 73 } 74 }mcmf; 75 const int maxN=100; 76 const int maxM=100; 77 int n,m,kase; 78 int Size[maxM]; 79 int p[maxN][maxN],tim[maxN][maxN],Time[maxN]; 80 int main(){ 81 kase=0; 82 while(scanf("%d%d",&m,&n)!=EOF&&(n||m)){ 83 if(kase){ 84 printf("\n"); 85 } 86 87 ++kase; 88 for(int i=1;i<=m;i++) 89 scanf("%d",&Size[i]); 90 for(int i=1;i<=n;i++){ 91 scanf("%d",&p[i][0]); 92 for(int j=1;j<=p[i][0];j++){ 93 scanf("%d%d",&p[i][j],&tim[i][j]); 94 } 95 } 96 mcmf.init(n+n*m+3); 97 for(int i=1;i<=n;i++){ 98 for(int j=1;j<=m;j++){ 99 if(p[i][1]>Size[j])continue; 100 int T=INF; 101 for(int l=1;l<=p[i][0];l++){ 102 if(p[i][l]<=Size[j]){ 103 T=min(T,tim[i][l]); 104 } 105 } 106 for(int l=1;l<=n;l++){ 107 mcmf.AddEdge(i,j*n+l,1,l*T); 108 } 109 } 110 } 111 for(int i=1;i<=n;i++) 112 mcmf.AddEdge(0,i,1,0); 113 for(int i=n+1;i<=n*m+n;i++) 114 mcmf.AddEdge(i,n*m+n+1,1,0); 115 long long res=mcmf.Mincost(0,n*m+n+1); 116 double ans=(double)res/n; 117 printf("Case %d\n",kase); 118 printf("Average turnaround time = %.2f\n",ans); 119 120 int ANS[maxN][maxN],bel[maxN]; 121 memset(ANS,-1,sizeof(ANS)); 122 memset(Time,0,sizeof(Time)); 123 for(int i=1;i<=n;i++){ 124 for(int j=mcmf.head[i];j!=-1;j=mcmf.Next[j]){ 125 if(j%2)continue; 126 if(mcmf.cap[j]>mcmf.flow[j])continue; 127 int v=mcmf.to[j]; 128 int a=v/n,b=v%n; 129 if(b==0){ 130 b=n; 131 a--; 132 } 133 ANS[a][b]=i; 134 bel[i]=a; 135 Time[i]=mcmf.cost[j]/b; 136 } 137 } 138 int F[maxN],T[maxN]; 139 for(int i=1;i<=m;i++){ 140 int st=0; 141 for(int j=n;j>=1;j--){ 142 if(ANS[i][j]!=-1){ 143 st=j; 144 break; 145 } 146 } 147 int all=0; 148 for(int j=st;j>=1;j--){ 149 int x=ANS[i][j]; 150 F[x]=all,T[x]=all+Time[x]; 151 all=T[x]; 152 } 153 } 154 155 for(int i=1;i<=n;i++){ 156 printf("Program %d runs in region %d from %d to %d\n",i,bel[i],F[i],T[i]); 157 } 158 } 159 return 0; 160 }