uvalive 2238 Fixed Partition Memory Management (KM)

这题建图很巧妙。

首先要读懂题意:

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值