其他分享
首页 > 其他分享> > #(抽象背包的转化)洛谷P1282 多米诺骨牌(提高+/省选-)

#(抽象背包的转化)洛谷P1282 多米诺骨牌(提高+/省选-)

作者:互联网

题目描述

多米诺骨牌有上下2个方块组成,每个方块中有1~6个点。现有排成行的

上方块中点数之和记为S1,下方块中点数之和记为S2,它们的差为|S1-S2|。例如在图8-1中,S1=6+1+1+1=9,S2=1+5+3+2=11,|S1-S2|=2。每个多米诺骨牌可以旋转180°,使得上下两个方块互换位置。 编程用最少的旋转次数使多米诺骨牌上下2行点数之差达到最小。

对于图中的例子,只要将最后一个多米诺骨牌旋转180°,可使上下2行点数之差为0。

输入格式

输入文件的第一行是一个正整数n(1≤n≤1000),表示多米诺骨牌数。接下来的n行表示n个多米诺骨牌的点数。每行有两个用空格隔开的正整数,表示多米诺骨牌上下方块中的点数a和b,且1≤a,b≤6。

输出格式

输出文件仅一行,包含一个整数。表示求得的最小旋转次数。

输入输出样例

输入 #1
4
6 1
1 5
1 3
1 2
输出 #1
1
分析:
这道题其实是一道十分抽象的背包问题
(你在逗我?这那里是背包,明明是贪心好咩?(≧∇≦)ノ)
真的是背包啊!QAQ
那么我们如何调整呢?
我们先把骨牌反转,使点数大的在上方;
这样保证上方的点数和一定大于下方的点数和;
那么翻转时的点数改变就是上下点数的差值乘以2了!
想到这里,我们可以考虑到:一开始的上下的点数的差值抽象成背包的体积,每一个骨牌当成一个物品,
因为我们一开始就把点数大的放在了上面,而每放一次,翻转次数就+1。考虑:要是我后来后悔了,我发现不翻这个骨牌更好怎么办?那我会把它翻回来,那么相当于没有翻这个骨牌。

因此,一开始翻过的骨牌重量就是-1,未翻过的骨牌重量就是1(重量等价于翻转次数)

当然,上下相同的骨牌就是体积为0,重量为0的物品,因为他们无论怎么翻,都不会对上下点数差造成影响。

至此,背包的模型就出来了。这个问题被简化成:有n个物品,给出每个物品的体积v[i],他们的重量是1或-1。背包的重量为base,体积为tot,现在请把这n个物品放到背包里去,总体积不能超过tot,体积最大的情况下使得物品重量之和最小。

其中,dp[i][j]表示前i件物品能装到体积为j的最小重量

vs[i][j]表示前i件物品能否装到j体积

#include<iostream>
using namespace std;
int n;
int dp[1005][6005];//dp[i][j]前i个物品填满j的背包
bool vs[1005][6005];//vs[i][j]标记使用前i个物品填满j的背包是否可行
int w[1005],base,tot;//w存储重量,base存储背包质量,tot为背包体积(初始最大差值)
int val[1005]; //val存储翻转每一个骨牌i所能提供的差值
inline int min(int x,int y)//
{
return x<y?x:y;
}
int main()
{
int x,y;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x>>y;
if(x>y)
{
val[i]=2*(x-y); //如果本身上大于下
w[i]=1;//翻转代价为1
tot+=x-y;//记录背包体积
}
if(y>x)
{
val[i]=2*(y-x);//本身下大于上
w[i]=-1;//若要翻转,等价于当初直接不翻转,也就是重量为-1
tot+=y-x;//更新体积
base++; //背包质量增加
}
}
for(int i=1;i<=n;i++)//以骨牌的顺序为阶段
for(int j=1;j<=tot;j++)//对于所有的背包体积,从小到大推
{
dp[i][j]=dp[i-1][j];//继承前一状态
vs[i][j]=vs[i-1][j];//
if( vs[i-1][j-val[i]] || j-val[i]==0)//如果翻转i号可以由i-1迁移过来
//只要存在前移状态或体积等于i骨牌的贡献度
if(!vs[i][j])// 若dp[i][j]未被定义
{//
dp[i][j]=dp[i-1][j-val[i]]+w[i];//直接更新并标记
vs[i][j]=1;//
}
else
dp[i][j]=min(dp[i][j],dp[i-1][j-val[i]]+w[i]);//否则,进行比较
//翻转第i张骨牌,从dp[i-1][j-val[i]]迁移来,加上重量即可

}
int tmp;
for(int i=tot;i>=1;i--)//从最大的体积倒着找,这样可以保证差值最小
if(vs[n][i])//
{
tmp=i;//

break;

}
cout<<base+dp[n][tmp];//由base加上改变状态到当前所需要的转移量,求得当前转移数量
return 0;
}

标签:背包,洛谷,val,省选,int,vs,P1282,点数,dp
来源: https://www.cnblogs.com/little-cute-hjr/p/11409562.html