[Poj1717]&[洛谷1282]多米诺骨牌 背包Dp
2017-08-31 21:41
267 查看
题目链接:Poj1717 , 洛谷1282 .
—————————————-
—————————————-
咦?每一组数要么不操作,要么只操作一次,和01背包怎么那么像?
那我们试着用01背包的套路来定义状态,看看可不可行。
设dp[i]表示使s1−s2=i的最小操作数,初始化dp[s1−s2]=0,其它为∞。为了方便理解,i可以取到负数。
设ai表示初始时第i组数的上半部分,bi表示下半部分,那么此时这一组数i的差为d=ai−bi。
不妨先令ai>bi,假如我们选择不交换,那么差还是d,假如交换就变成了−d,s1−s2减少了2×d。
所以我们可以得到状态转移方程:dp[i]=dp[i+2×d]+1.
对于ai<bi,交换之后会使s1−s2增加2×d,那么转移方程则是:dp[i]=dp[i−2×d]+1.
最后统计答案的时候就从dp[0]开始左右扩展,假如有dp[i]不为∞,那么答案就是dp[i].
差不多就是这样了?好像的确没有后效性。
至于i的下标会取到负数,我们可以将数组整体向数轴正半轴平移,这样就能使所有的i取到正数了。
—————————————-
—————————————-
—————————————-
——wrote by miraclejzd.
—————————————-
概述
给你n对数,每一对数分为上下两部分,这两个部分各有一个值,你可以交换上下两个数的位置。记上部分的和为s1,下部分的和为s2,现求使得|s1−s2|达到最小的交换次数。—————————————-
题解
由题目的叙述我们可以知道,每一对数要么不交换,要交换最多被交换一次,因为再交换一次就会换回来。咦?每一组数要么不操作,要么只操作一次,和01背包怎么那么像?
那我们试着用01背包的套路来定义状态,看看可不可行。
设dp[i]表示使s1−s2=i的最小操作数,初始化dp[s1−s2]=0,其它为∞。为了方便理解,i可以取到负数。
设ai表示初始时第i组数的上半部分,bi表示下半部分,那么此时这一组数i的差为d=ai−bi。
不妨先令ai>bi,假如我们选择不交换,那么差还是d,假如交换就变成了−d,s1−s2减少了2×d。
所以我们可以得到状态转移方程:dp[i]=dp[i+2×d]+1.
对于ai<bi,交换之后会使s1−s2增加2×d,那么转移方程则是:dp[i]=dp[i−2×d]+1.
最后统计答案的时候就从dp[0]开始左右扩展,假如有dp[i]不为∞,那么答案就是dp[i].
差不多就是这样了?好像的确没有后效性。
至于i的下标会取到负数,我们可以将数组整体向数轴正半轴平移,这样就能使所有的i取到正数了。
—————————————-
代码
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #include<vector> #define ll long long #define For(i,j,k) for(register int i=j; i<=(int)k; ++i) #define Forr(i,j,k) for(register int i=j; i>=(int)k; --i) #define INF 0x3f3f3f3f using namespace std; const int maxn = 1000+5; const int num = 5050; int n, s, Ans; int a[maxn], dp[(maxn*6)<<1]; inline void read(int &x){ char c; while((c=getchar())<'0' || c>'9'); x = c^48; while((c=getchar())>='0' && c<='9') x = x*10+(c^48); } inline void chkmin(int &x, int y){ if(y < x) x = y; } int main(){ int x, y, d; read(n); For(i, 1, n){ read(x), read(y); s += (a[i] = x-y); } memset(dp, INF, sizeof(dp)); dp[s+num] = 0;//初始化,加num即让数组整体向右平移. For(i, 1, n) if(!a[i]) continue; else if(a[i] > 0){//ai > bi 的情况. d = a[i]<<1; For(j, -5000+num, 5000-d+num)//5000为s1-s2的最大值. chkmin(dp[j], dp[j+d]+1); } else{//ai < bi 的情况. d = (-a[i])<<1; Forr(j, 5000+num, -5000+d+num) chkmin(dp[j], dp[j-d]+1); } Ans = INF; if(dp[num] != INF) Ans = dp[num]; else{ For(i, 1, 5000){ if(dp[num-i] != INF) chkmin(Ans, dp[num-i]); if(dp[num+i] != INF) chkmin(Ans, dp[num+i]); if(Ans != INF) break; }//从dp[0]开始左右扩展,统计答案. } printf("%d", Ans); return 0; }
—————————————-
小结
此题考察的算法其实比较基础,重点在于分析问题原本的性质,即每一组数只有(不交换\交换一次)这两种选择。当分析出这题的本质就是01背包时,解法自然而然就出来了。—————————————-
——wrote by miraclejzd.
相关文章推荐
- 洛谷 P1282 多米诺骨牌 (背包dp)
- [POJ1717][luogu1282]Dominoes(多米诺骨牌)(dp)
- TELE (树形DP&背包扩展) #by Plato
- 【基础练习】【背包DP】洛谷1164 小A点菜题解
- DP&背包问题
- HDU 2069 & UVA 674 Coin Change(换硬币 dp 入门经典水题,背包问题)
- DP<背包?> POJ 1015
- DP&背包问题
- poj2063 & hdu1963 Investment 又是DP啦,,经典完全背包问题
- DP<01 背包> POJ 1837
- 【例题】【背包DP&高精度】NKOJ3819 奶牛商店
- 洛谷 1654 产品排序(sort) 背包DP+贪心
- ACM学习历程—HDU 1059 Dividing(dp && 多重背包)
- hdu4739 Zhuge Liang's Mines 状态压缩dp,0-1背包
- 洛谷P1373 小a和uim之大逃离[背包DP]
- HDU5389:Zero Escape(dp & 类背包)
- 洛谷P1060 开心的金明(DP,0-1背包)
- [背包DP] [Luogu P1282] 多米诺骨牌
- POJ1155-树形DP&&背包
- 洛谷 P1782 旅行商的背包(二进制优化下的DP)