P1282 多米诺骨牌 /// DP

题目大意:

https://www.luogu.org/problemnew/show/P1282

题解

当知道 总和 及 单行的和
就可以算出差值的大小
1 2 3 4(单行和l=10)
8 7 6 5
(总和s=36)
则上下行的差值为 |l-(s-l)|

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
int n,a[1005],b[1005],dp[1005][5005];
/// dp[i][j]到第i块牌时 使单行和为j至少需要翻牌的次数
int main()
{
    while(~scanf("%d",&n)) {
        int s=0;
        for(int i=0;i<n;i++) {
            scanf("%d%d",&a[i],&b[i]);
            s+=a[i]+b[i]; /// 计算总和
        } // 因为牌上的数最大为6 最小为1 则差值的范围为 0到5
        memset(dp,INF,sizeof(dp));
        dp[0][b[0]]=1; dp[0][a[0]]=0;
        for(int i=1;i<n;i++)
            for(int j=0;j<=5*n;j++) { /// n块牌各种单行和的翻牌情况
                if(j-a[i]>=0) dp[i][j]=min(dp[i][j],dp[i-1][j-a[i]]);
                /// a[i]在上面 则从j-a[i]转移到j 不需要翻牌
                if(j-b[i]>=0) dp[i][j]=min(dp[i][j],dp[i-1][j-b[i]]+1);
                /// b[i]在下面 则从j-b[i]转移到j需要翻牌 应该+1
            }

        int minc=INF, mins=INF;
        for(int i=0;i<=5*n && i<=s;i++)
            if(dp[n-1][i]!=INF) { // 单行和为i的情况存在
                if(fabs(i-(s-i))<mins) { // 这种情况得到的上下行差值更小
                    mins=fabs(i-(s-i)); 
                    minc=dp[n-1][i];
                }
                else if(fabs(i-(s-i))==mins)
                    minc=min(minc,dp[n-1][i]);
            }

        printf("%d\n",minc);
    }

    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/zquzjx/p/9327091.html