多米诺骨牌 题解

1.题目大意

给出n个多米诺骨牌,每个牌上下两个块都有1~6个点,上方块中点数之和记为S1,下方块中
点数之和记为S2,我们可以将第i张牌翻转,让它上下两个块的点数翻转,现在
求|S1-S2|使最小的最小交换次数

2.思路思想

   首先我们可以知道s1+s2是可以确定的,且我们只用确定s1或s2就可以知道另一部分
的值,也就可以知道它们的差值。则我们就可以讨论s1的值或s2的值
   我们设dp[i][j]表示用前i个牌上半部分(也就是s1)为j的最小翻拍次数(这样就与
   01背包差不多了)
   也就有:
   	dp[i][j]=(dp[i-1][j-a[i].up],dp[i-1][j])
   	a[i].up表示第i个牌的上边块的点数,如果不交换,则要构成j就是前一张牌的j-a[i].up加上a[i].up
   	如果要交换,则就是
   	dp[i][j]=min(dp[i][j],dp[i-1][j-y[i]]+1
   	因为交换所以要+1
   	**我们要求最小,就要初始化成极大值,再去取min,而边界就是dp[0][0]=0(显而易见)**
   	tips:因为这里有减法,所以只有减去为非负才执行,也可以用顺推

3.代码

有两种方法,一种是递推,亦可以记忆化
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
int n , dp[1002][6006] , d[1003] , x[1003] , y[1003] , up_  , down_;//定义,可以用滚动
int main()
{
    scanf( "%d" , &n );
    for( int i = 1 ; i <= n ; i ++ ){
        scanf( "%d%d" , &x[i] , &y[i] );
        up_ += max(x[i] , y[i]);//可以知道它最大也大不过所有所有牌的最大点数和,把它做成
        down_ += min( y[i] , x[i] );//j变量的控制
    }
    memset( dp , 0x3f , sizeof( dp ) );
    dp[0][0] = 0;//边界
    for( int  i = 1 ; i <= n ; i ++ ){
        for( int j = up_  ; j >= 1 ; j -- ){//01背包是从大到小
            if(j >= x[i] )
                if( dp[i-1][j-x[i]] < 0x3f3f3f3f )
                    dp[i][j] = min( dp[i][j] , dp[i-1][j-x[i]] );
            if( j >= y[i] && dp[i-1][j-y[i]] < 0x3f3f3f3f )
                dp[i][j] = min( dp[i][j] , dp[i-1][j-y[i]] + 1 );
        }//这个就是递推式
    }
    int minn = 0x3f3f3f3f , k=0 ;
    for( int j = 0 ; j <= up_ ; j ++ )
        if( dp[n][j] < 0x3f3f3f3f ){//来找|S1-S2|最小值,存步数
            int m = fabs( up_ + down_ - j - j );
            int m1 = fabs( up_ + down_ - minn - minn );
            if( m < m1 ){
                k = dp[n][j];
                minn = j;
            }
            else if( m == m1 && dp[n][j] < k ){
                k = dp[n][j];
                m = m1;
            }
        }
    printf( "%d" , k );//输出
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43823476/article/details/84582617
今日推荐