UVALive 2238 Fixed Partition Memory Management(二分完美匹配)
2013-08-31 21:41
513 查看
题意:计算机中有一些固定大小的内存,内存越大,处理速度越快。对于一个程序,加入不同的内存空间,处理所需时间不同。现给出m个内存空间,n个程序,对于每个程序程序,有k组数据(s,t),分别表示当程序 i 在某个内存环境s下对应的运行时间t。当有一个内存空间为si,一个程序的2组数据a,b,满足as<=si<bs,那么这个程序放到该内存中的运行时间是at(取下界)。已知不同的内存空间可以同时运行,同一个内存空间的程序在某一时刻只能有一个在运行。又知道一个程序从提交给计算机到运行结束这段时间为“运行时间”(所有程序在0时刻同时提交)。求出最小平均“运行时间”=总“运行时间”/n个程序,并给出每个程序在哪个内存区内运行(内存区编号1~m),以及该程序在内存区内是从何时开始运行,何时结束的。(保证不会出现程序的最小需求内存>最大内存空间)
分析:
很复杂的题意,直观感觉就是所求的量很多,而且并非直接求最小值,是要具体的把方案求解出来。想到这里应该能确定用图论解决。进一步的,要用什么来处理,排除法也能想到匹配(其实我在刷图论专题= =)。
确定了算法,就明确了方向,一侧必然是n个程序。那么另一侧应该是什么呢?我们求的是平均“运行时间”,那么可行顶标Lx、Ly之和就应该是最小的总“运行时间”。我们先考虑总运行时间应该怎么算呢?假设一个内存空间 i 按顺序运行3个程序a、b、c,那么这个程序总的运行时间Ti=at+(at+bt)+(at+bt+ct)=3*at+2*bt+1*ct
(至此应该都能想到,接下来根据这个式子建模)。
我们发现,a程序虽然是第一个完成,但它受这个内存中程序的数量以及运行次序的影响,实际贡献了3*at的时间。当我们拿到一个程序a,并放入某个内存 i 中。首先根据程序的 k 组数据可以确定at的大小。如果假定这个内存中有x个程序,a程序排在第y个,于是构造一个点(i,x,y),那么我们就可以确定边权为(x-y+1)*at。
如此构造有何不妥之处呢?
做最佳二分完美匹配,每个程序都对应于一个点(i,x,y),得到最小值。会不会不合常理呢?会的...因为是在交错树上沿等边增广,这里每次调整后,都必然出现许多等边((x-y+1)的大小同时受x和y影响),虽然调整交错树可能会出现多条等边,但那是巧合(数据相等),而非必然。这种必然导致的就是出现了大量的符合最小值的方案,一个任务,可以匹配点(i,x,y),也可以匹配点(i,(x+1),(y+1)),反正都在内存 i 内,at为定值。而这种任意分配是不合理的,因为最后无法确定顺序:一个内存中,若有x个程序,它的(i,x,1)可能都已经匹配,但是本该与(i,x,2)(i,x,3)匹配的点却与(i,(x-1),1)(i,(x-1),2)匹配。
建模失败了,那么有什么可取之处?该如何改进呢?
先找问题:1、点的构造决定了边权(x-y+1)*at,x、y同时影响是等边过多;2、时间复杂度高:一侧有S=n个点,一侧有T=m*n*n=mn2个点,所以总复杂度S*T*(S+T)=m2n5=10的10次方 = =
重新建点,将x,y两个变量简化为一个变量,观察式子Ti=3*at+2*bt+ct,从后往前逐次+1;又边权(x-y+1)*at 中,(x-y+1)本身就表示的是系数,也可以理解为倒数第几个。于是,新的构点取(i,p):在第 i 个内存中以倒数第p个的身份出现,边权就是p*at。
同样做最佳完美匹配。已知一种完美匹配是任意的,不仅不一定是最佳,而且不一定不合理。但是最佳二分完美匹配,一定是合理的。因为(i,p)与权值p*at相互唯一对应,杜绝了之前多条等边的情况。又有,若在内存 i 中有3个程序a,b,c,分别对应( i,1)( i,2)( i,4),必然不是最佳,c对应于( i,3)有“更佳”的权值3*ct。再考虑复杂度,S=n,T=m*n,总复杂度S*T*(S+T)=m2n3=10的7次方,可以接受。
注意:
1、S,T两侧的数组因为点数的不同,要分开处理。
2、关于n个程序与m*n个点( i,p),究竟放那一侧,我选择n个程序在S侧。其实若放T侧,貌似可以直接顺序找到每个程序对应的点( i,p),但还要处理每个程序在内存中的具体时间,复杂度上是相同的。
3、我这样处理其实也存在弊端,这是另一种(n个点在T侧)所能避免的,真亏我能想出来。
固定内存 i ,查找p,从1找到第一个为0的点之前截止,这些点对应的 left[] 就是放在内存 i 中的程序编号。但可能n个程序都在同一个内存中,所以p的上界要定在(n+1)而不是n;这又引发了另一个问题:若n个程序都在最后一个内存中,所找到的(n+1)对应的点是第(m*n+1)个点,超出了既定的数据范围,若初始化右侧数据只是[1,m*n]的话,(m*n+1)这个点会访问到之前的数据。
View Code
分析:
很复杂的题意,直观感觉就是所求的量很多,而且并非直接求最小值,是要具体的把方案求解出来。想到这里应该能确定用图论解决。进一步的,要用什么来处理,排除法也能想到匹配(其实我在刷图论专题= =)。
确定了算法,就明确了方向,一侧必然是n个程序。那么另一侧应该是什么呢?我们求的是平均“运行时间”,那么可行顶标Lx、Ly之和就应该是最小的总“运行时间”。我们先考虑总运行时间应该怎么算呢?假设一个内存空间 i 按顺序运行3个程序a、b、c,那么这个程序总的运行时间Ti=at+(at+bt)+(at+bt+ct)=3*at+2*bt+1*ct
(至此应该都能想到,接下来根据这个式子建模)。
我们发现,a程序虽然是第一个完成,但它受这个内存中程序的数量以及运行次序的影响,实际贡献了3*at的时间。当我们拿到一个程序a,并放入某个内存 i 中。首先根据程序的 k 组数据可以确定at的大小。如果假定这个内存中有x个程序,a程序排在第y个,于是构造一个点(i,x,y),那么我们就可以确定边权为(x-y+1)*at。
如此构造有何不妥之处呢?
做最佳二分完美匹配,每个程序都对应于一个点(i,x,y),得到最小值。会不会不合常理呢?会的...因为是在交错树上沿等边增广,这里每次调整后,都必然出现许多等边((x-y+1)的大小同时受x和y影响),虽然调整交错树可能会出现多条等边,但那是巧合(数据相等),而非必然。这种必然导致的就是出现了大量的符合最小值的方案,一个任务,可以匹配点(i,x,y),也可以匹配点(i,(x+1),(y+1)),反正都在内存 i 内,at为定值。而这种任意分配是不合理的,因为最后无法确定顺序:一个内存中,若有x个程序,它的(i,x,1)可能都已经匹配,但是本该与(i,x,2)(i,x,3)匹配的点却与(i,(x-1),1)(i,(x-1),2)匹配。
建模失败了,那么有什么可取之处?该如何改进呢?
先找问题:1、点的构造决定了边权(x-y+1)*at,x、y同时影响是等边过多;2、时间复杂度高:一侧有S=n个点,一侧有T=m*n*n=mn2个点,所以总复杂度S*T*(S+T)=m2n5=10的10次方 = =
重新建点,将x,y两个变量简化为一个变量,观察式子Ti=3*at+2*bt+ct,从后往前逐次+1;又边权(x-y+1)*at 中,(x-y+1)本身就表示的是系数,也可以理解为倒数第几个。于是,新的构点取(i,p):在第 i 个内存中以倒数第p个的身份出现,边权就是p*at。
同样做最佳完美匹配。已知一种完美匹配是任意的,不仅不一定是最佳,而且不一定不合理。但是最佳二分完美匹配,一定是合理的。因为(i,p)与权值p*at相互唯一对应,杜绝了之前多条等边的情况。又有,若在内存 i 中有3个程序a,b,c,分别对应( i,1)( i,2)( i,4),必然不是最佳,c对应于( i,3)有“更佳”的权值3*ct。再考虑复杂度,S=n,T=m*n,总复杂度S*T*(S+T)=m2n3=10的7次方,可以接受。
注意:
1、S,T两侧的数组因为点数的不同,要分开处理。
2、关于n个程序与m*n个点( i,p),究竟放那一侧,我选择n个程序在S侧。其实若放T侧,貌似可以直接顺序找到每个程序对应的点( i,p),但还要处理每个程序在内存中的具体时间,复杂度上是相同的。
3、我这样处理其实也存在弊端,这是另一种(n个点在T侧)所能避免的,真亏我能想出来。
固定内存 i ,查找p,从1找到第一个为0的点之前截止,这些点对应的 left[] 就是放在内存 i 中的程序编号。但可能n个程序都在同一个内存中,所以p的上界要定在(n+1)而不是n;这又引发了另一个问题:若n个程序都在最后一个内存中,所找到的(n+1)对应的点是第(m*n+1)个点,超出了既定的数据范围,若初始化右侧数据只是[1,m*n]的话,(m*n+1)这个点会访问到之前的数据。
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> #define clr(a,m) memset(a,m,sizeof(a)) #define rep(i,a,b) for(int i=a;i<=b;i++) #define rrep(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int MAXN=55; const int MAXM=11; const int INF =1e9; struct Point{ int c,t; Point(){} Point(int _c,int _t):c(_c),t(_t){} bool operator < (const Point res)const { return c<res.c; } }; struct Run{ int num,l,r; Run(){} Run(int _num,int _l,int _r):num(_num),l(_l),r(_r){} }run[MAXN]; int time[MAXM]; int mem[MAXM],gap[MAXN][MAXN*MAXM];//数组忘记改大小了 vector<Point>p[MAXN]; int n,m,cnt; int Lx[MAXN],S[MAXN]; int Ly[MAXN*MAXM],T[MAXN*MAXM],left[MAXN*MAXM],slack[MAXN*MAXM]; void init() { rep(i,1,n) p[i].clear(); } void read() { init(); rep(i,1,m) scanf("%d",&mem[i]); rep(i,1,n){ int k,c,t; scanf("%d",&k); rep(j,1,k){ scanf("%d%d",&c,&t); p[i].push_back(Point(c,t)); } } rep(i,1,n){ int sz=p[i].size(); rep(j,1,m){ int tt; if(mem[j]<p[i][0].c) tt=INF; else{ int x=upper_bound(p[i].begin(),p[i].end(),Point(mem[j],0))-p[i].begin(); tt=p[i][x-1].t; } rep(p,1,n){ if(tt==INF)gap[i][(j-1)*n+p]=-INF; else gap[i][(j-1)*n+p]=-p*tt; } } } } bool match(int u) { S[u]=true; rep(v,1,n*m) if(!T[v]){ int tmp=Lx[u]+Ly[v]-gap[u][v]; if(tmp==0){ T[v]=true; if(!left[v]||match(left[v])){ left[v]=u; return true; } }else slack[v]=min(slack[v],tmp); } return false; } void update() { int a=INF; rep(v,1,n*m)//!! if(!T[v]) if(a>slack[v]){ a=slack[v]; } rep(i,1,n) if(S[i])Lx[i]-=a; rep(i,1,n*m)//!! if(T[i])Ly[i]+=a; } void KM() { rep(i,1,n){ Lx[i]=-INF; rep(j,1,n*m) if(Lx[i]<gap[i][j]){ Lx[i]=gap[i][j]; } } rep(i,1,n*m+1) left[i]=Ly[i]=0; rep(i,1,n){ rep(j,1,n*m) slack[j]=INF; while(1) { rep(j,1,n) S[j]=0; rep(j,1,n*m) T[j]=0; if(match(i)) break; else{ update(); } } } } void print() { printf("Case %d\n",++cnt); int ans=0; rep(i,1,n) ans+=Lx[i]; rep(i,1,n*m) ans+=Ly[i]; printf("Average turnaround time = %.2f\n",-ans*1.0/n); clr(time,0); rep(i,1,m){ int sz; rep(p,1,n+1)//!会不会全都在同一个内存区?放在最后一个内存区,找到的+1会越界找到上一组数据 if(left[(i-1)*n+p]==0){ sz=p-1; break; } rrep(p,sz,1){ int x=left[(i-1)*n+p]; run[x]=Run(i,time[i],time[i]-gap[x][(i-1)*n+p]/p); time[i]-=gap[x][(i-1)*n+p]/p; } } rep(i,1,n) printf("Program %d runs in region %d from %d to %d\n",i,run[i].num,run[i].l,run[i].r); } int main() { cnt=0; while(~scanf("%d%d",&m,&n)) { if(!n&&!m) return 0; if(cnt)puts(""); read(); KM(); print(); } return 0; } /* 2 4 40 60 1 35 4 1 20 3 1 40 10 1 60 7 2 2 20 40 2 20 100 40 2 2 20 100 40 3 */
View Code
相关文章推荐
- uvalive 2238 Fixed Partition Memory Management (KM)
- Fixed Partition Memory Management UVALive - 2238 建图很巧妙 km算法左右顶点个数不等模板以及需要注意的问题 求最小权匹配
- UVALive 2238 Fixed Partition Memory Management(建图、KM)
- UVALive 2238 Fixed Partition Memory Management 固定分区内存管理(KM算法,变形)
- UVALive2238 Fixed Partition Memory Management
- uva 1006 - Fixed Partition Memory Management(完美匹配)
- UVa1006 - Fixed Partition Memory Management
- LA2238 Fixed Partition Memory Management
- C - Underwater Snipers UVALive - 5000 最小值最大化问题 二分
- UVALive 3890 (半平面交 二分)
- UVALive 6275 (ACM-ICPC Live Archive: 6275 ) Joint Venture(二分)
- UVALive 6168 Fat Ninjas --二分小数+搜索
- UVALive 3211 Now or later(2-SAT,二分,Kosaraju)
- UVALive 4625 Garlands(二分答案 + DP)
- POJ 3968|UVALive 4992|HDU 3761|UVA 1475|Jungle Outpost|二分|半平面交
- 【UVALive 4642】Malfatti Circles(圆,二分)
- UVALive 7261 Xiongnu's Land(二分)
- UVA Live 3211飞机调度问题-二分+2-SAT
- UVaLive 4254 Processor (二分+优先队列)
- UVALive 4683 Find The Number(容斥+二分)