题目描述
把长度为
l
1
,
l
2
.
.
.
l
n
l_1,l_2...l_n
l1,l2...ln的
n
n
n个程序放在磁带
T
1
,
T
2
T_1,T_2
T1,T2上,并且希望按照使用最大检索时间取得最小值的方式存储,即如果存放在
T
1
,
T
2
T_1,T_2
T1,T2上的程序集合分别为
A
A
A和
B
B
B,则希望所选择的
A
A
A和
B
B
B使得
m
a
x
{
∑
i
∈
A
l
i
,
∑
i
∈
B
l
i
}
max\{\sum_{i\in{A}} l_i ,\sum_{i\in{B}} l_i \}
max{i∈A∑li,i∈B∑li}取得最小值。
可以采用动态规划法或者回溯法,这里采用回溯法。
这里给出2个测试样例
样例1:
输入
8
1 3 6 7 5 4 8 4
输出
7 8 4
1 3 6 5 4
样例2:
输入
7
5 8 9 4 1 2 5
输出
9 1 2 5
5 8 4
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define FOR(i,s,e) for (int i=(s); i<(e); i++)
#define FOE(i,s,e) for (int i=(s); i<=(e); i++)
#define FOD(i,s,e) for (int i=(s); i>=(e); i--)
const int maxn=1e6;
int n;//程序的数目
int len[maxn];//程序段长度数组l[i]
int f[maxn],bestx[maxn],C,bestp=0;
int seta[maxn],setb[maxn]; //存放分配程序的A,B集合
int sum=0;//程序长度总和
void backtrack(int i,int cp) //回溯法
{
if(i>n){//回溯的边界条件
if(cp>bestp){//更新最优值
bestp=cp;
for(i=0;i<=n;i++)
bestx[i]=f[i];
}
}
else{
FOE(j,0,1){//j=1 取进A集合
f[i]=j;//取舍标志
if(cp+j*len[i]<=C){
cp+=len[i]*j;
backtrack(i+1,cp);
cp-=len[i]*j;
}
}
}
}
void distribution(int len[], int seta[], int setb[],int f[])
{//将程序分配到A,B中
backtrack(1,0);
seta[0] = setb[0] = 0;//initialize the count of A and B
FOE(i,1,n){
if(bestx[i] == 1) seta[++seta[0]] = len[i];
else setb[++setb[0]] = len[i];
}
}
int main()
{
cin>>n;
//len[0] = n;
for(int i=1; i<=n; i++)
{
cin>>len[i];
sum+=len[i];
}
C = sum/2;//要使A,B集合长度和最大值最小,最理想的情况是总和平分
distribution(len,seta,setb,f);
//输出A,B磁盘存放程序情况
FOE(i,1,seta[0]){
cout<<seta[i];
if(i==seta[0]) cout<<endl;
else cout<<" ";
}
FOE(i,1,setb[0]){
cout<<setb[i];
if(i==setb[0]) cout<<endl;
else cout<<" ";
}
return 0;
}