这道题貌似有多种解法,就从我考试时的思路说起吧。见到这道题我首先想到的就是区间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",×,&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]);
}
}
小编云:动归一定要大脑清醒,不要手残+脑残,不要想当然