World Finals >> 2001
二分图:
左边结点为任务,右边结点表示 第几块内存区域的倒数第几个任务。
详见代码:
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
#include<vector>
#include<map>
#include<sstream>
#include<set>
#include<stack>
#include<cctype>
#include<utility>
#pragma comment(linker, "/STACK:102400000,102400000")
#define PI 3.1415926535897932384626
#define eps 1e-7
#define sqr(x) ((x)*(x))
#define FOR0(i,n) for(int i=0 ;i<(n) ;i++)
#define FOR1(i,n) for(int i=1 ;i<=(n) ;i++)
#define FORD(i,n) for(int i=(n) ;i>=0 ;i--)
#define lson num<<1,le,mid
#define rson num<<1|1,mid+1,ri
#define MID int mid=(le+ri)>>1
#define zero(x)((x>0? x:-x)<1e-15)
#define mk make_pair
#define _f first
#define _s second
using namespace std;
//const int INF= ;
typedef long long ll;
//const ll inf =1000000000000000;//1e15;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//fin.close();
//fout.close();
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
const ll INF =1000000000000000;
const int maxn= 50+10 ;
const int maxm= 10+3 ;
//by yskysker123
ll siz[maxm]; //maxn是任务最大数目,maxm是内存块最大数目
ll s[maxn][12];
ll t[maxn][12];
ll cost[maxn][maxm];//[任务编号][内存编号]
int num[maxn];
int lef[maxn*maxm];
ll lx[maxn],ly[maxn*maxm];
bool S[maxn],T[maxn*maxm];
int reg[maxn];
int pri[maxn];
ll w[maxn][maxn*maxm];
int n,m;
int cnt;//表示 右边的结点数目,右边的结点[maxm][maxn]:[内存块编号][该内存块执行的倒数第几个任务]
ll ans;
ll tim[maxm][maxn];
int work(ll si,int pth,int le,int ri)//内存块大小,任务编号,二分区域le、ri,确定该大小內存执行该任务所花时间。
{
//二分,在该任物的s1之前加个s0=0,t0=INF(无穷),
// 找到比内存块大小 小,且差值最小的si。由此确定该大小內存执行该任务所花时间
// cout<<si<<" "<<pth<<" "<<le<<" "<<ri<<endl;
while(le<=ri)
{
int mid=(le+ri)/2;
if( si >=s[pth][mid] ) le=mid+1;
else ri=mid-1;
}
// cout<<"pth "<<ri<<endl;
return ri;
}
//带权二分匹配:
bool match(int i)
{
S[i]=true;
for(int j=1;j<=cnt;j++)
{
if(lx[i]+ly[j]==w[i][j]&&!T[j])
{
T[j]=1;
if(!lef[j]||match(lef[j]))
{
lef[j]=i;
return true;
}
}
}
return false;
}
void update()
{
ll a=INF;
for(int i=1;i<=n;i++) if(S[i])
for(int j=1;j<=cnt;j++) if(!T[j])
a=min(a,lx[i]+ly[j]-w[i][j]);
for(int i=1;i<=n;i++)
{
if(S[i]) lx[i]-=a;
}
for(int i=1;i<=cnt;i++)
{
if(T[i]) ly[i]+=a;
}
}
void KM()
{
for(int i=1;i<=n;i++)
lx[i]=- INF;
for(int i=1;i<=cnt;i++)
ly[i]=lef[i]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=cnt;j++)
{
lx[i]=max(lx[i],w[i][j]);
}
// cout<<"lx["<<i<<"] "<<lx[i]<<endl;;
}
for(int i=1;i<=n;i++)
{
while(1)
{
for(int j=1;j<=n;j++) S[j]=0;
for(int j=1;j<=cnt;j++) T[j]=0;
if(match(i)) break;
else update();
}
}
memset(tim,0,sizeof tim);
ans=0;
for(int i=cnt;i>=1;i--)
{
// cout<<"y: "<<i <<endl;
if(!lef[i]) continue; //错误写法:if(!T[i]) continue;
// cout<<"aaaa"<<endl;
int p=lef[i]; //任务编号
int th=(i-1)/n +1; //内存编号
reg[p]= th;// 任务对应内存
int thvp= (i-1)%n +1;// 倒数第几个操作
// cout<<"task :"<<p<<endl;
// cout<<"ans add"<<endl;
// cout<<-w[p][i]<<endl;
ans-= w[p][i];
// cout<<"ans = "<<ans<<endl;
tim[th][thvp]=tim[th][thvp+1]+cost[p][th];//第th个内存区域,执行它的倒数第thvp个任务的结束时间。
pri[p]=thvp;// 顺序编号(倒数)
}
// cout<<ans<<endl;
// cout<<ans<<endl;
}
int main()
{
int kase=0;
while(~scanf("%d%d",&m,&n)&&(m||n))
{
// cout<<"region "<<m<<"program "<<n<<endl;
for(int i=1;i<=m;i++)
{
scanf("%lld",&siz[i]);
}
int k;
for(int i=1;i<=n;i++)
{
scanf("%d",&k);
num[i]=k;
for(int j=1;j<=k;j++)
{
scanf("%lld%lld",&s[i][j],&t[i][j]);
}
s[i][0]=0; //方便二分
t[i][0]=INF;//方便二分
}
for(int i=1;i<=n;i++)
{
cnt=0;
for(int j=1;j<=m;j++)
{
// cout<<"task :"<<i<<" ram: "<<j<<endl;
int tmp=work(siz[j],i,0,num[i]);
cost[i][j] =t[i][ tmp ];
// cout<<cost[i][j]<<endl;
for(int p=1;p<=n;p++)
{
cnt++;
w[i][cnt]=-p*cost[i][j];
// cout<<"w[i][cnt]"<<endl;
// cout<<w[i][cnt]<<endl;
}
}
}
KM();
printf("Case %d\n",++kase);
printf("Average turnaround time = %.2lf\n",ans*1.0/n );
for(int i=1;i<=n;i++)
{
printf("Program %d runs in region %d from %lld to %lld\n",i,reg[i],tim[ reg[i]][pri[i]+1],tim[reg[i]][ pri[i] ]);
}
putchar('\n');
}
return 0;
}