某不知名的比赛#1 B题 洛谷 P1282 多米诺骨牌

简化题意:给出n(n<=1000)个整数的序列a,b(1<=a[i],b[i]<=6),问最少多少次交换后,可以使a的和与b的和的差最小。

通过观察发现对于每一组a[i],b[i],只有交换与不交换两种可能,暴力枚举是O(2^{n})级的,这时候就要请到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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值