洛谷 P1282 多米诺骨牌("01"背包)

传送门

https://www.cnblogs.com/violet-acmer/p/9852294.html

参考资料:

  [1]:https://blog.csdn.net/Darost/article/details/52517823

题解:

  对于一个牌,无非就是翻转或者不翻转这两种情况,所以由此我们可以从决策入手

  设dp[i][j]为前i个骨牌差值为j的最小翻牌次数

  初始值全部赋值为INF;

  然后f[1][a[1]-b[1]]=0, f[1][b[1]-a[i]]=1;对应于第一张牌不翻和翻

  状态转移方程:

  f[i][j] = min(f[i-1][j-(a[i]-b[i])], f[i-1][j-(b[i]-a[i])]+1)]分别对应于不翻和翻

  由于有负数,要加上一个较大的中间数把所有的值转为正数,然后找答案在dp[n]里找,从较大的中间数开始双向查找,最早找到的就是答案。

学习笔记:

  此题难点在于状态转移方程的查找。 

  dp[ ][ ]的作用很巧妙,是我不曾想到的,初始思路只知道一个暴力,就是枚举所有的可能,从中查找最优解,时间复杂度高达O(2^n),指定不能高效求解。

  算法之路还很长,一步一步来吧

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define mem(a,b) memset(a,b,sizeof(a))
 6 #define INF 0x3f3f3f3f
 7 const int maxn=1000+50;
 8 
 9 int n;
10 int a[maxn],b[maxn];
11 int dp[maxn][10*maxn];
12 int mid=5000;//中间数
13 
14 void Solve()
15 {
16     dp[1][a[1]-b[1]+mid]=0,dp[1][b[1]-a[1]+mid]=1;
17     for(int i=2;i <= n;++i)
18     {
19         for(int j=-5*n;j <= 5*n;++j)//查找dp[i][-5n,....,5n]的最优解
20         {
21             dp[i][j+mid]=min(dp[i][j+mid],dp[i-1][j-(a[i]-b[i])+mid]);
22             dp[i][j+mid]=min(dp[i][j+mid],dp[i-1][j-(b[i]-a[i])+mid]+1);
23         }
24     }
25     for(int i=mid,j=mid;i <= 5*mid || j >= 0;++i,--j)
26         if(dp[n][i] != INF)
27         {
28             printf("%d
",dp[n][i]);
29             break;
30         }
31         else if(dp[n][j] != INF)
32         {
33             printf("%d
",dp[n][j]);
34             break;
35         }
36 
37 }
38 int main()
39 {
40     scanf("%d",&n);
41     for(int i=1;i <= n;++i)
42         scanf("%d%d",a+i,b+i);
43     mem(dp,INF);
44     Solve();
45 }
View Code
原文地址:https://www.cnblogs.com/violet-acmer/p/9879834.html