题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=239
题目大意:有m个内存空间,n个程序要跑,每个程序要跑都要一定的内存空间,所以对于每个程序,他有 k 个si、ti,si < s(i+1) ,表示如果 si<=mem < s(i+1),它的时间是 ti。如果比 s0 还要小,就是说明不能运行。让你求出调度他们的方案,使得所有程序的 平均结束时间最小。输出这个最小的平均时间和调度方案。
解题思路:假设一个内存区域,按顺序执行k个程序,时间是 t1、t2、t3 ... tk,所有程序的结束时间和是 k*t1 + (k-1)*t2 + (k-2)*t3 + ... + tk,也就是,如果在内存块 i 某个程序 i 是倒数第 p 个运行的,那么他的贡献值是p*t(i,j)。
所以说我们构造二分图,左边是 n 个程序,右边是 n*m 个点,表示 m 个内存,倒数第 1~n 。边的权值为 p*t(i,j)。 这样建完之后就只要求最小权匹配即可。注意:并不是所有的匹配都对应一个合法方案,因为有的是只有倒数第二,没有倒数第一。但是,我们求得最佳匹配是不会出现这种情况的,肯定是合法的,因为要乘一个系数,肯定是乘越小越好,就是先有倒数第一,才会有倒数第二,看看后面的输出就知道了。
这道题先是TLE了好久,就是不知道什么原因,后来才发现,原来后面乘系数的时候,我无法运行标的是 INF,后面系数直接拿来乘了,结果爆 int 了。。。 然后又 WA 了好久,一点一点查,才发现,是用来编号的 id 数组我开成 [ MAX_N ][ MAX_M ] 了,应该是 [ MAX_M ][ MAX_N ]。。= =
代码如下:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff;
const int MAX_N = 55;
const int MAX_M = 11;
int id[MAX_M][MAX_N];
int nn;
int get_id(int i,int j)
{
if(!id[i][j]) return id[i][j] = ++nn;
return id[i][j];
}
void get_id_init()
{
memset(id,0,sizeof(id));
nn = 0;
}
vector<pair<int,int> > p[MAX_N];
int mem[MAX_M];
int w[MAX_N][MAX_N*MAX_M],time[MAX_N][MAX_M];
void build(int n,int m)
{
get_id_init();
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
{
/*if(mem[j] < p[i][0].first)
time[i][j] = INF;
else
{
int x = upper_bound(p[i].begin(),p[i].end(),make_pair(mem[j],INF))-p[i].begin();
time[i][j] = p[i][x-1].second;
}
//printf("i = %d,j = %d,time = %d\n",i,j,time[i][j]);*/
for(int k = 1;k <= n;k++)
{
if(time[i][j] >= INF) w[i][get_id(j,k)] = -INF;
else w[i][get_id(j,k)] = -time[i][j]*k;
}
}
}
struct KM
{
int n1,n2;
int s[MAX_N],t[MAX_N*MAX_M],left[MAX_N*MAX_M];
int lx[MAX_N],ly[MAX_N*MAX_M],slack[MAX_N*MAX_M];
void init(int a,int b)
{
n1 = a;n2 = b;
}
int match(int i)
{
s[i] = 1;
for(int j = 1;j <= n2;j++)
if(!t[j])
{
int tmp = lx[i]+ly[j]-w[i][j];
if(tmp == 0)
{
t[j] = 1;
if(!left[j] || match(left[j]))
{
left[j] = i;
return 1;
}
}
else slack[j] = min(slack[j],tmp);
}
return 0;
}
void update()
{
int a = INF;
for(int i = 1;i <= n2;i++)
if(!t[i]) a = min(a,slack[i]);
for(int i = 1;i <= n1;i++)
if(s[i]) lx[i] -= a;
for(int i = 1;i <= n2;i++)
if(t[i]) ly[i] += a;
}
void solve()
{
for(int i = 1;i <= n1;i++)
{
lx[i] = -INF;
for(int j = 1;j <= n2;j++)
lx[i] = max(lx[i],w[i][j]);
}
for(int i = 1;i <= n2;i++)
left[i] = ly[i] = 0;
for(int i = 1;i <= n1;i++)
{
for(int j = 1;j <= n2;j++)
slack[j] = INF;
while(1)
{
for(int j = 1;j <= n1;j++) s[j] = 0;
for(int j = 1;j <= n2;j++) t[j] = 0;
if(match(i)) break;
else update();
}
}
}
}km;
int pp[MAX_N][5];
void print(int n,int m)
{
int sum = 0;
for(int i = 1;i <= km.n2;i++)
if(km.left[i])
{
int pro = km.left[i];
//printf("%d---%d\n",pro,i);
sum += w[pro][i];
}
double avg = -sum*1.0/n;
printf("Average turnaround time = %.2f\n",avg);
for(int i = 1;i <= m;i++)
{
/*
int cc = id[i][1];
for(;cc <= id[i][n];cc++) if(km.left[cc] == 0) break;
cc--;
*/
int cc = id[i][n];
for(;cc >= id[i][1];cc--) if(km.left[cc]) break;
int ss = 0,tt = 0;
for(int j = cc;j >= id[i][1];j--)
{
int pro = km.left[j];
tt = ss+time[pro][i];
pp[pro][0] = i;
pp[pro][1] = ss;
pp[pro][2] = tt;
ss = tt;
}
}
for(int i = 1;i <= n;i++)
printf("Program %d runs in region %d from %d to %d\n",i,pp[i][0],pp[i][1],pp[i][2]);
puts("");
}
int main()
{
int cas = 0;
int n,m;
while(~scanf("%d%d",&m,&n) && (n+m))
{
for(int i = 1;i <= m;i++)
scanf("%d",&mem[i]);
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
time[i][j] = INF;
for(int i = 1;i <= n;i++)
{
p[i].clear();
int k;
scanf("%d",&k);
while(k--)
{
int s,t;
scanf("%d%d",&s,&t);
//p[i].push_back(make_pair(s,t));
for(int l = 1;l <= m;l++)
if(mem[l] >= s) time[i][l] = t;
}
}
build(n,m);
km.init(n,nn);
km.solve();
printf("Case %d\n",++cas);
print(n,m);
}
return 0;
}
/*
2 4
40 60
1 35 4
1 20 3
1 40 10
1 60 7
2 3
40 60
1 35 4
1 20 3
1 40 10
*/
还有要求最小权和匹配,其实我先开始的做法是模仿最大子图那样,做最小子图,即选择权值最小的边作为开始顶标,然后也是一样模仿的做,可以证明这样也行。
代码如下:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff;
const int MAX_N = 55;
const int MAX_M = 11;
int id[MAX_M][MAX_N];
int nn;
int get_id(int i,int j)
{
if(!id[i][j]) return id[i][j] = ++nn;
return id[i][j];
}
void get_id_init()
{
memset(id,0,sizeof(id));
nn = 0;
}
vector<pair<int,int> > p[MAX_N];
int mem[MAX_M];
int w[MAX_N][MAX_N*MAX_M],time[MAX_N][MAX_M];
void build(int n,int m)
{
get_id_init();
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
{
if(mem[j] < p[i][0].first)
time[i][j] = INF;
else
{
int x = upper_bound(p[i].begin(),p[i].end(),make_pair(mem[j],INF))-p[i].begin();
time[i][j] = p[i][x-1].second;
}
//printf("i = %d,j = %d,time = %d\n",i,j,time[i][j]);
for(int k = 1;k <= n;k++)
{
if(time[i][j] >= INF) w[i][get_id(j,k)] = INF;
else w[i][get_id(j,k)] = time[i][j]*k;
}
}
}
struct KM
{
int n1,n2;
int s[MAX_N],t[MAX_N*MAX_M],left[MAX_N*MAX_M];
int lx[MAX_N],ly[MAX_N*MAX_M],slack[MAX_N*MAX_M];
void init(int a,int b)
{
n1 = a;n2 = b;
}
int match(int i)
{
s[i] = 1;
for(int j = 1;j <= n2;j++)
if(!t[j])
{
int tmp = lx[i]+ly[j]-w[i][j];
if(tmp == 0)
{
t[j] = 1;
if(!left[j] || match(left[j]))
{
left[j] = i;
return 1;
}
}
else slack[j] = min(slack[j],abs(tmp));
}
return 0;
}
void update()
{
int a = INF;
for(int i = 1;i <= n2;i++)
if(!t[i]) a = min(a,slack[i]);
for(int i = 1;i <= n1;i++)
if(s[i]) lx[i] += a;
for(int i = 1;i <= n2;i++)
if(t[i]) ly[i] -= a;
}
void solve()
{
for(int i = 1;i <= n1;i++)
{
lx[i] = INF;
for(int j = 1;j <= n2;j++)
lx[i] = min(lx[i],w[i][j]);
}
for(int i = 1;i <= n2;i++)
left[i] = ly[i] = 0;
for(int i = 1;i <= n1;i++)
{
for(int j = 1;j <= n2;j++)
slack[j] = INF;
while(1)
{
for(int j = 1;j <= n1;j++) s[j] = 0;
for(int j = 1;j <= n2;j++) t[j] = 0;
if(match(i)) break;
else update();
}
}
}
}km;
int pp[MAX_N][5];
void print(int n,int m)
{
int sum = 0;
for(int i = 1;i <= km.n2;i++)
if(km.left[i])
{
int pro = km.left[i];
//printf("%d---%d\n",pro,i);
sum += w[pro][i];
}
double avg = sum*1.0/n;
printf("Average turnaround time = %.2f\n",avg);
for(int i = 1;i <= m;i++)
{
int cc = id[i][1];
for(;cc <= id[i][n];cc++) if(km.left[cc] == 0) break;
cc--;
int ss = 0,tt = 0;
for(int j = cc;j >= id[i][1];j--)
{
int pro = km.left[j];
tt = ss+time[pro][i];
pp[pro][0] = i;
pp[pro][1] = ss;
pp[pro][2] = tt;
ss = tt;
}
}
for(int i = 1;i <= n;i++)
printf("Program %d runs in region %d from %d to %d\n",i,pp[i][0],pp[i][1],pp[i][2]);
puts("");
}
int main()
{
int cas = 0;
int n,m;
while(~scanf("%d%d",&m,&n) && (n+m))
{
for(int i = 1;i <= m;i++)
scanf("%d",&mem[i]);
for(int i = 1;i <= n;i++)
{
p[i].clear();
int k;
scanf("%d",&k);
while(k--)
{
int s,t;
scanf("%d%d",&s,&t);
p[i].push_back(make_pair(s,t));
}
}
build(n,m);
km.init(n,nn);
km.solve();
printf("Case %d\n",++cas);
print(n,m);
}
return 0;
}
/*
2 4
40 60
1 35 4
1 20 3
1 40 10
1 60 7
2 3
40 60
1 35 4
1 20 3
1 40 10
*/