hdu 5691 Sitting in Line(百度之星A轮) 状压_旅行商问题变型
2016-05-22 23:06
253 查看
下面例子对应的图片:
![](http://img.blog.csdn.net/20160522231052410?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
/* 2016.5.22 22:43 hdu5691 题意:给定n个数,及其对应的位置,位置为-1,表示位置任意,否则其位置是固定的。 1 <= n <= 16 求max(a1 * a2 + a2 * a3 + a3 * a4 + ... + an-1 * an) 分析: 使用状态dp,旅行商问题的变型。 首先对数字按输入的顺序编号,第一个输入的编号0,第二个输入的编号1,... 对于状态i,用二进制形式表示(从低位到高位依次编号,与上述编号的数字一一对应)。 如果该位是1,则表示该编号的数字被选择。假设该状态中1的个数有x个,这x个数与排列队伍中的前x个一一对应。 1. 如果第(x - 1)个位置,也就是最后一个位置,已经被占据,那么当前只能以占据该位置的数字编号结尾。 不以该编号结尾的状态都是非法的,即j不等于该编号,dp[i][j]非法,可能当前状态i的编号位不为1,即i & (1 << 编号) == 0 此时所有状态都非法。 dp[i][j] 表示将编号j放到x - 1位。 2. 如果第(x - 1)个位置可以填任意编号,那么该位也只能填那些没有固定位置的编号,有固定位置的编号是非法的, dp[i][j],如果编号j被占用(在其他位置,不能用于第(x - 1)位),非法。 对于合法的状态i,以j作为第(x - 1)个位置的编号,那么另一种状态i - (1 << j),同样以一种合法的编号k结尾, 则dp[i][j] = max(dp[i - (1 << j)][k] + a[k] * a[j],所有合法的结尾编号k); 3. 特别注意只有一个1的情况,此时状态合法则置0,作为初始化。 说一下为什么要多一维,表示该状态以哪编号结尾: (1) 事实上,我们完全可以 dp[i] = max(dp[i], dp[i - (1 << j)] + a[k] * a[j],其中k(select[i - (1 << j)]) 为dp[i - (1 << j)]取得最大值时对应的结尾编号,枚举所有合法j) 此时状态i有对应的最优选择select[i] 这时候k直接确定,是唯一的,不需要另外枚举。这种做法忽视了动态规划的特点:无后效性,后一项a[j]显然依赖于a[k]。 虽然我们保证了dp[i - (1 << j)]是以所有合法编号结尾中值最大的,但是却不能保证这个k是合适的,例如 这时候a[k]是所有合法结尾中数值最小的,但a[j]却非常大,显然如果a[k]能够大一点,则a[k] * a[j]占主要比重, 将使得dp[i]的值越大。当然,这种缺陷可能可以通过枚举j来弥补。 (2) 为了方便找到a[k],同时也枚举了(i - (1 << j))的所有可能的结尾,来计算a[k] * a[j],这种计算是相当全面的。 (3) 相当于一条相连接的路径,一步一步走。 举例: 1 4 10 1 5 -1 20 -1 15 2 编号10 --> 0 5 --> 1 20 --> 2 15 --> 3 vis[0] = 1, vis[3] = 1,即编号0和编号3都已经固定,不能随意使用到其他位置。 显然pos[1] = 0, pos[2] = 3,即位置1被编号0占据,位置2被编号3占据 对所有二进制状态求值: 1. 把编号0放到位置0 dp[1][0] = 非法,因为编号0已经有主... 2. 把编号1放到位置0,合法,因为编号1无主,且位置0没被占据。 dp[10][1] = 0 3. 把编号0和1放到位置0和1 3.1 把编号0放到位置1 dp[11][0] = dp[10][1] + a[1] * a[0] = 50 3.2 把编号1放到位置1 dp[11][1] = dp[01][0] + a[0] * a[1],非法,因为编号0不能放到位置0 4. 把编号2放到位置0,合法 dp[100][2] = 0 5. 把编号0和2放到位置0和1 5.1 把编号0放到位置1,合法,因为编号0固定在位置1 dp[101][0] = dp[100][2] + a[2] * a[0] = 200 5.2 把编号2放到位置1,不合法,因为位置1已经有了编号0 dp[101][2] = dp[001][0] + a[0] * a[2] 非法 6. 把编号1和2放到位置0和1 6.1 把编号1放到位置1,非法,因为位置1已经有编号0 dp[110][1] = dp[100][2] + a[2] * a[1]非法 6.2 把编号2放到位置1,非法,因为位置1已经有编号0 dp[110][2] = dp[010][1] + a[1] * a[2]非法 7. 把编号0,1,2放到位置0, 1, 2 7.1 把编号0放到位置2,非法,编号0已经有主 dp[111][0] = dp[110][1] + a[1] * a[0] dp[110][2] + a[2] * a[0] 7.2 把编号1放到位置2,非法,因为位置2已经有编号3 dp[111][1] 7.3 把编号2放到位置2,非法,因为位置2已经有编号3 dp[111][2] 8. 把编号3放到位置0 dp[1000][3]非法,因为编号3已经有主 9. 把编号0和3放到位置位置0和1 9.1 把编号0放到位置1,合法,编号0固定在位置1 dp[1001][1] = dp[1000][3] + a[3] * a[1]非法 9.2 把编号3放到位置1,非法,编号3已经固定在位置2 dp[1001][3] = dp[0001][0] + a[0] * a[3]非法 10. 把编号1和3放到位置0和1 10.1 把编号1放到位置1,非法,位置1已经有编号0 dp[1010][1] = dp[1000][3] + a[3] * a[1]非法 10.2 把编号3放到位置1,非法,位置1已经有编号0 dp[1010][3] = dp[0010][1] + a[1] * a[3]非法 11. 把编号0,1,3放到位置0,1,2 10.1 把编号0放到位置2,位置2已经有编号3了 dp[1011][0] = dp[1010][1] dp[1010][3]都非法 10.2 把编号1放到位置2,位置2已经有编号3了 dp[1011][1] = dp[1001][0] dp[1001][3]都非法 10.3 把编号3放到位置2,合法 dp[1011][3] = dp[0011][0] + a[0] * a[3] = 50 + 10 * 15 = 200 (位置0,1,2依次为5, 10, 15) dp[0011][1] 非法 12. 把编号2和3放到位置0和1 12.1 把编号2放到位置1,非法,位置1已经有编号0 dp[1100][2] = dp[1000][3] + a[3] * a[2] 12.2 把编号3放到位置1,非法,位置1已经有编号0 dp[1100][3] = dp[0100][2] + a[2] * a[3] 13. 把编号0,2,3放到位置0,1,2 13.1 把编号0放到位置2,非法,编号0已经固定在位置1,且位置2只能放置编号3 dp[1101][0] 13.2 把编号2放到位置2,非法,位置2只能放置编号3 dp[1101][2] 13.3 把编号3放到位置2,合法 dp[1101][3] = dp[0101][0] + a[0] * a[3] = 200 + 10 * 15 = 350(位置0,1,2依次为20, 10, 15) dp[0101][2]非法 14. 把编号1,2,3放到位置0,1,2 14.1 把编号1放到位置2,非法,位置2只能放置编号3 dp[1110][1] 14.2 把编号2放到位置2,非法,位置2只能放置编号3 dp[1110][2] 14.3 把编号3放到位置2,合法 dp[1110][3] = dp[0110][1] dp[0110][2]都非法 15. 把编号0,1,2,3放到位置0,1,2,3 15.1 把编号0放到位置3,非法,编号0固定在位置1 dp[1111][0] 15.2 把编号1放到位置3,合法 dp[1111][1] = dp[1101][0]非法 dp[1101][2]非法 dp[1101][3] + a[3] * a[1] = 350 + 15 * 5 = 425(位置0,1,2依次为20, 10, 15, 5) 15.3 把编号2放到位置3,合法 dp[1111][2] = dp[1011][0]非法 dp[1011][1]非法 dp[1011][3] + a[3] * a[2] = 200 + 15 * 20 = 500 (位置0,1,2依次为5, 10, 15, 20) 15.4 把编号3放到位置3,非法,编号3固定在位置2 dp[1111][3] 最后的结果为max(dp[1111][0~3]) = 500 */ #include <cstdio> #include <climits> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 16 + 1; int dp[1 << MAXN][MAXN], pos[MAXN], a[MAXN], b[MAXN], vis[MAXN]; //计算data中二进制1的个数 int getBits(int data){ int res = 0; while(data){ res += (data & 1); data >>= 1; } return res; } int main(){ int t, n, i, up, j, k, cnt, ans, cas = 1; scanf("%d", &t); while(t--){ scanf("%d", &n); fill(pos, pos + n, INT_MIN); memset(vis, 0, sizeof(vis)); for(i = 0; i < n; i++){ scanf("%d%d", &a[i], &b[i]); if(b[i] >= 0){ pos[b[i]] = i;//位置b[i]已经被编号i占据 vis[i] = 1;//编号i不能随意使用 } } up = 1 << n; for(i = 1; i < up; i++){ cnt = getBits(i);//获取二进制状态i中1的个数 for(j = 0; j < n; j++){ if(!(i & (1 << j))){ continue; } dp[i][j] = INT_MIN;//不满足条件者,或者没被下面更新者,都是不合法状态 if(cnt == 1){ //位置0没被占据,且编号j没被使用,或者位置0固定放置编号j,则编号j可以放到位置0 if(pos[0] == INT_MIN && !vis[j] || pos[0] == j){ dp[i][j] = 0; } //printf("i = %d, j = %d, dp = %d \n", i, j, dp[i][j]); break; } //位置cnt - 1没被占据,且编号j没被使用,或者位置cnt - 1固定放置编号j,则编号j可以放到位置cnt - 1 if(pos[cnt - 1] == INT_MIN && !vis[j] || j == pos[cnt - 1]){ for(k = 0; k < n; k++){ //枚举i - (1 << j)中的二进制1(编号),作为结尾,不能与j相同 //且该状态必须合法 if(k == j || !((1 << k) & i) || dp[i - (1 << j)][k] == INT_MIN){ continue; } dp[i][j] = max(dp[i][j], dp[i - (1 << j)][k] + a[k] * a[j]); } } //printf("i=%d, j = %d, dp = %d \n", i, j, dp[i][j]); } //puts(""); } //求取最终所有位置排满时的最值。 ans = INT_MIN; for(i = 0; i < n; i++){ ans = max(ans, dp[up - 1][i]); } printf("Case #%d:\n%d\n", cas++, ans); } return 0; }
相关文章推荐
- 详解Android应用中屏幕尺寸的获取及dp和px值的转换
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- Android dpi,dip,dp的概念以及屏幕适配
- Android px、dp、sp之间相互转换
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- DP问题各种模型的状态转移方程
- POJ-1695-Magazine Delivery-dp
- nyoj-1216-整理图书-dp
- TYVJ1193 括号序列解题报告
- 对DP的一点感想
- TYVJ上一些DP的解题报告
- soj1005. Roll Playing Games
- 01背包问题