工件调度

3、工件调度(work.pas/c/cpp)
【问题描述】 Jam的工厂新进了一批货,需要赶紧加工,工厂有许多加工机器,对于这些货物来说是绰绰有余了,但是他想合理安排加工方式,使每个加工机器都被充分利用。例如:他希望工件在 8小时内加工完成,已知这里有 5个工件,按顺序,需要消耗的时间依次为 5 2 4 4 3,即 5 个工件要在 8小时内完工。当然,你可以调度它们分别在 5台加工机器上完成,但是每台机器就依次有 3 6 4 4 5小时是空闲的,那么机器的加工利用率就很低了。这里我们用剩余时间的平方和代表你调度的加工利用率,即32+62+42+42+52=102,当然,Jam希望这个值越小越好,样才能使机器被充分利用。另外同一个机器加工完一个工件后需要休息一个小时!工件的加工是按输入所给定的顺序的,

请注意: 1加工的顺序是给定的不能把后面的工件拿到前面去加工,如图中的 2 3不能调换位置,只能依次按给定的顺序调度。这一点请引起注意!

 2.休息时间为 l 小时,对于这个例子,如果 Jam要求在 7个小时内完成,则 5号工件就必须在另一个机器上完成。当然,题目保证 Jam要求韵时间一定比消耗最长时间的工件要长,否则就无解了。 

3.你可以视机器的数量是无限的,对于一个调度,你想要几个机器加工都可以,只要总效率最高,也就是效率值最小。

4.任何工件都应该向前安排,只能在工件完成后才留出空闲时间,这一点,图中也有体现。 

【输入文件】(work.in) 

输入文件的第一行分别是Jam要求完工的时间和工件的数量第二行分别为一号工件所需时间,二号工件所需时间输入顺序就是给定顺序 

【输出文件】(work.out)输出文件为两行,第一行为最小的效率值,第二行至以下为调度方案。 

【输入样例】 

8 5

5 2 4 4 3 

【输出样例】 

10

5

2 4 

4 3 

【注释】输出的方案应使工件尽量向前调度,比如:

对于输入 

8 3

5 2

5

输出应该为 

5 2 

而不是 

9

2  5

【数据规模】 对于 40%的数据有: 工件数量<10 Jam 要求时间<30对于 100%的数据有: 工件数量<500 Jam 要求时间<30000数据保证效率值在长整型范围内




















































【数据规模】 对于 40%的数据有: 工件数量<10 Jam 要求时间<30对于 100%的数据有: 工件数量<500 Jam 要求时间<30000数据保证效率值在长整型范围内

这道题貌似有多种解法,就从我考试时的思路说起吧。见到这道题我首先想到的就是区间DP。但是当时犯了个脑残错误,导致没能AC,一下是试后改正后的代码。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int i,j,k,n,m,a[503],times,sum[503][503],ans,p;
int f[503][503],g[503][503];
int s[503][503],num;
void dfs(int x,int len)//把工件按最低加工利用率的分配方法分开
{
	if (x==0||len==0)
	 return;
	dfs(x-g[x][len],len-1);
    num++; int j=0;
	for (int i=x-g[x][len]+1;i<=x;i++)
	 j++,s[num][j]=a[i];
    s[num][0]=g[x][len];
}
int main()
{
	freopen("work.in","r",stdin);
	freopen("work.out","w",stdout);
	scanf("%d%d",&times,&n);//times表示每个机器工作的时间,n表示工件数
	for (i=1;i<=n;i++)
	 scanf("%d",&a[i]);
	for (i=1;i<=n-1;i++)//预处理sum数组,sum[i][j]表示从i到j加工所需的时间
	 for (j=i;j<=n;j++)
	  sum[i][j]=sum[i][j-1]+a[j];
	<span style="background-color: rgb(255, 255, 255);"><span style="color:#6600cc;">sum[n][n]=a[n];//当时手残忘了处理最后一个了,T_T</span></span>
    memset(f,127/3,sizeof(f));
    f[0][0]=0;
    for (i=1;i<=n;i++)
     f[0][i]=0;
    for (i=1;i<=n;i++)
     if (sum[1][i]+i-1<=times)
      {
      	f[i][1]=(sum[1][i]+i-1-times)*(sum[1][i]+i-1-times);
      	g[i][1]=i;
      }
     else
      break;
	for (j=2;j<=n;j++)//一共用了多少台机器
	 for (i=j;i<=n;i++)//当前工件区间的起点
	  for (k=1;k<=i-j+1;k++)//当前工件区间的长度
	   if (sum[i-k+1][i]+k-1<=times)//判断当前工件区间能否放在一个机器上
	   {
	   	 int t=f[i-k][j-1]+(sum[i-k+1][i]+k-1-times)*(sum[i-k+1][i]+k-1-times);
	   	 if (f[i][j]>t||f[i][j]==t&&<span style="color:#6600cc;">g[i][j]>k</span>)//这句话很重要,因为题目中说输出的方案应使工件尽量向前调度,所以后面的长度应尽量短
	   	  {
	   	  	f[i][j]=t; g[i][j]=k; //f[i][j]表示到工件I,用了J台机器的最低加工利用率,G[I][J]存储区间长度,便于输出
	   	  }
	   }
	ans=1000000000;
	p=0;
	for (i=1;i<=n;i++)
	 if (ans>f[n][i])
	  {
	  ans=min(ans,f[n][i]);
	  p=i;
      }
	printf("%d\n",ans);
	dfs(n,p);
    for (i=1;i<=num;i++)
     {
     	for (j=1;j<=s[i][0];j++)
     	 printf("%d ",s[i][j]);
     	printf("\n");
     }
	return 0;
}
还有另一种思路,其实也比较好理解。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[503][503],s[503][503],len[503],loap[503];
int i,j,k,m,n,l;
void dfs(int i,int j)//用二分法不断寻找应该换机器的工件
{
	if (i==j)  l++;
	else
	 {
	 	int x;
	 	if (s[i][j]==0)
	 	 for (x=i;x<=j;x++) l++;
	 	else
	 	 {
	 	 	if (i<=s[i][j])
	 	 	 dfs(i,s[i][j]);
	 	 	loap[l+1]=1;
	 	 	if (s[i][j]+1<=j)
	 	 	 dfs(s[i][j]+1,j);
	 	 }
	 }
	
}
int main()
{
	freopen("work.in","r",stdin);
	freopen("work.out","w",stdout);
	scanf("%d%d",&m,&n);
	for (i=1;i<=n;i++)
	 scanf("%d",&len[i]),dp[i][i]=len[i],loap[i]=0;
	for (i=1;i<n;i++)
	 for (j=1;j<=n-i;j++)
	  k=i+j,dp[j][k]=dp[j][k-1]+len[k];
	for (i=0;i<n;i++)//i表示区间长度,dp[j][k]表示工件j到k的加工利用率 
	 for (j=1;j<=n-i;j++)
	  {
	  	k=i+j;
	  	int tmp=m-dp[j][k]-k+j;
	  	if (tmp<0)  dp[j][k]=2000000000;//判断区间j到k能否放到一个机器上 
	    else  dp[j][k]=tmp*tmp;
	  }
	for (i=0;i<n;i++)
	 for (j=1;j<=n-i;j++)
	  {
	  	k=i+j;
	  	for (int l=k-1;l>=j;l--)
	  	 if (dp[j][l]<2000000000&&dp[l+1][k]<2000000000)
	  	  if (dp[j][l]+dp[l+1][k]<dp[j][k])
	  	   dp[j][k]=dp[j][l]+dp[l+1][k],s[j][k]=l;//s[j][k]记录中间值 
	  }
    printf("%d\n",dp[1][n]);
    l=0;
	dfs(1,n);
	for (i=1;i<=n;i++)
	 {
	 	if (loap[i]==1) printf("\n");
	 	printf("%d ",len[i]);
	 }	
}


小编云:动归一定要大脑清醒,不要手残+脑残,不要想当然




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值