题意:n个人n<=100,每个人有权值a[i]<=450,要求把n个人分成两组,人数差<=1&&权值差最小
dp(i)[j][k]前i个人选j个是否能凑出权值为k? n为奇数可以选half or half-1 n为偶数 只能选half个 复杂度O(n*n*M)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1e2+20;
const int M=455;
int dp[N][N*M];//dp(i)[j][k]前i个人选j个是否能凑出权值为k?
int n,a[N];
int main()
{
while(cin>>n)
{
int sum=0,s;
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=n;i++)
cin>>a[i],sum+=a[i];
int half=(n+1)/2;
s=sum;
sum/=2;
for(int i=1;i<=n;i++)
{
//逆推保证dp[j-1][k-a[i]]是前i-1个人中选出的
for(int j=half;j>=1;j--)
{
for(int k=sum;k>=a[i];k--)
{
//cout<<j<<' '<<a[i]<<' '<<k<<endl;
dp[j][k]|=dp[j-1][k-a[i]];
}
}
}
int j;
for(j=sum;j>=0;j--)
{
if(n%2)
{
if(dp[half][j]||dp[half-1][j])
break;
}
else
{
if(dp[half][j])
break;
}
}
cout<<min(j,s-j)<<' '<<max(j,s-j)<<endl;
}
return 0;
}
随机化:步骤一:随机分成两组 步骤二:不断随机交换两个数 若更优 则交换 否则继续随机直到一定次数后返回步骤1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define N 102
int n,m,a[N],s[N],t[N];
int main(){
int i,j,sum,s1,s2,T=250,temp1,temp2,len1,len2,flag,res1=0;//T是随机的实验组数,亲测245WA,250AC
s1 = s2 = sum = 0;
scanf("%d",&n);
for(i = 0;i<n;i++){
scanf("%d",&a[i]);
sum += a[i];
}
if(n==1){
printf("0 %d\n",a[0]);
return 0;
}
m = n - n/2;
n /= 2;
while(T--){
len1 = len2 = flag = s1 = s2 = 0;
for(i = 0;len1<n && len2<m;i++){//将数据随机分成两份
if(rand()%2){
t[len2++] = a[i];
s2 += a[i];
}
else{
s[len1++] = a[i];
s1 += a[i];
}
}
while(len1 < n){
s1 += a[i];
s[len1++] = a[i++];
}
while(len2 < m){
s2 += a[i];
t[len2++] = a[i++];
}
while(flag<10){
i = rand()%n;//在两份数据中任选一个进行调换
j = rand()%m;
temp1 = abs(s1 - s2);
temp2 = abs(s1 - s2 - 2*(s[i]-t[j]));
if(temp1 > temp2){//如果调换结果更好,那么进行调换
flag = 0;
s1 -= s[i]-t[j];
s2 -= t[j]-s[i];
temp1 = s[i];
s[i] = t[j];
t[j] = temp1;
}
flag++;//flag保存有多少次没有进行过更新了
}
res1 = max(res1,min(s1,s2));
}
printf("%d %d\n",res1,sum-res1);
return 0;
}