洛谷 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\n",dp[n][i]); 29 break; 30 } 31 else if(dp[n][j] != INF) 32 { 33 printf("%d\n",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 }