poj 1717 Dominoes

/*
  Name: poj 1717 Dominoes
  Author:UnimenSun
  Date: 3/07/11 21:54
  Description: 变形0-1背包
*/

/*
解题报告:
该题为变形的0-1背包,如果能做到很好的转化解决起来不是很难
这里将gap值做为背包容量(注意这里gap有可能为负值, 可以给其加上一个定值,将负值变为正值),用反转一次骨牌对gap的影响值
change[i]做为物品往背包里放,同样这个影响值,既价值同样有存在负值的情况,与是按0-1背包的思想可得到转移方程:
dp[i][j] = min(dp[i-1][j], dp[i-1][j-change[i]]+1):其中i为骨牌号,j为gap值
这里需要注意动规数组存放的值是意思,这一点必须弄清,其实该数组中存放的值为:要使差值改变gap最少的反转次数(这里要好好理解)
正是由于动规数组里存放着这样的值,因此在最后只要找到一个这样的值就行:与fix+gap最接近的i,同时dp[fix+gap-i]或dp[fix+gap+i]有值即可,注意
如果对称着都存在值,取出两者中最小的即可
*/

#include <iostream>
#include <algorithm>
using namespace std;
const int nmax = 19870711;
const int fix = 20000;

int n;
int dp[35000];
int up, down, gap;
int change[1010];
int nright, nletf;

int main()
{
	int i, j;
	while(cin>>n)
	{
		//初始化
		gap = 0;
		nright = nletf = fix;
		for(i=fix-n*12; i<=fix+n*12; ++i)
			dp[i] = nmax;
		dp[fix] = 0; 
		
		//读入数据
		for(i=1; i<=n; ++i)
		{
			cin>>up>>down;
			change[i] = (up - down) * 2;
			gap += (up - down);
		}

		//0-1背包处理
		for(i=1; i<=n; ++i)
		{
			if(change[i] > 0)
				for(j=nright+change[i]; j>=nletf+change[i]; --j)
					dp[j] = _cpp_min(dp[j], dp[j-change[i]]+1);
			if(change[i] < 0)
				for(j=nletf+change[i]; j<=nright+change[i]; ++j)
					dp[j] = _cpp_min(dp[j], dp[j-change[i]]+1);
			nright = _cpp_max(nright, nright+change[i]);
			nletf = _cpp_min(nletf, nletf+change[i]);
		}
		
		//找最优解
		int ans;
		for(i=0; i<=n*12; ++i)
		{
			if(dp[fix+gap-i]<nmax || dp[fix+gap+i]<nmax)
			{
				ans = _cpp_min(dp[fix+gap-i], dp[fix+gap+i]);
				break;		
			}
		} 
		cout<<ans<<endl;
	}
	return 0;
}

你可能感兴趣的:(poj 1717 Dominoes)