简化题意:给出n(n<=1000)个整数的序列a,b(1<=a[i],b[i]<=6),问最少多少次交换后,可以使a的和与b的和的差最小。
通过观察发现对于每一组a[i],b[i],只有交换与不交换两种可能,暴力枚举是O()级的,这时候就要请到DP了。
我刚开始的设想是f[i][0]表示在第i组时不交换时的总交换次数,f[i][1]表示在第i组时交换的总交换次数差。
但是我发现还要记录目前a与b的差,而交不交换不需要记录。
所以f[i][j]表示在第i组差为j时的交换次数。
注意到j可能为负,所以我们需要给整体加上一个数
设a全是6,b全是1,此时最小j=(6-1)*n=5n<=5000
具体见代码:(附注释)
#include<bits/stdc++.h>
using namespace std;
int n,ans;
int N = 5000; //全体加的量
int a[1010],b[1010];
int f[1010][10010]; //第二维是因为差最大是5000,还要加上N
int main(){
cin>>n;
for(int i = 1; i <= n; i++) cin>>a[i]>>b[i];
memset(f,0x7F,sizeof(f)); //初始化!这很重要!
f[0][N] = 0;
for(int i = 1; i <= n; i++){
for(int j = -5000; j <= 5000; j++){
int dif = a[i] - b[i];
f[i][j + N] = min(f[i - 1][j - dif + N],f[i - 1][j + dif + N] + 1);
}
}
for(int i = 0; i <= 5000; i++){
ans = min(f[n][i + N],f[n][N - i]); //找最小,两边(正负)一起找,别忘记加上N
if(ans <= 1000){ //因为 n<=1000
cout<<ans;
break;
}
}
return 0;
}