HDU_3828 A + B problem 状态dp
来源:互联网 发布:医疗器械软件评估报告 编辑:程序博客网 时间:2024/04/30 02:15
http://acm.hdu.edu.cn/showproblem.php?pid=3828
题意:
给你N个数,求N个数在满足下面3个条件的情况下相加的最小和。
条件一: 相加的两个数是二进制相加;
条件二:相加的两个数A,B , A的后缀可以和B的前缀合并成一个
条件三:相加的两个数A,B,如果A是B的子串,则A可以不算。
思路:
很好的一道状态压缩dp。对于两个数,我们可以发现,如果这两个数都不是各自的子串,那个
这两个数相加,要是A前B后,或者B前A后,只有这两种情况。这样分析了之后我们似乎就可以
推导出本题是否可以用贪心的求法,也就是说每次都是求一个值最小的那个。但是这个贪心思路
是不正确的,那么我们就可以考虑dp,但是线性的dp是不满足最优子性质的, 那么我们就可以用
状态压缩来记录当前的状态。因此本题的做法是这样的:用dp[i][j]表示状态为j,以i为前缀的最小
长度,求出最小长度之后通过构造最小串就可以得出问题的解了。
#include<string>#include<iostream>#include<algorithm>#include<string.h>#include <cstdio>#define MIN(a,b) ( (a)>(b)?(b):(a) )using namespace std;typedef __int64 LL ;const LL Mod = 1000000009 ;int n , N;const int MM = 16 ;string word[MM] ;int ll[MM][MM] ;int dp[MM][1<<16] ;string ans ;void init(){ LL a ; for(int i=0;i<n;i++){ cin >> a ; word[i] = "" ; while( a ){ if( a&1 ) word[i] += '1' ; else word[i] += '0' ; a >>= 1 ; } reverse( word[i].begin() , word[i].end() ); }}void move(){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ if( i!=j && word[j].find( word[i] ) != string::npos ){ word[i--] = word[ -- n ] ; break ; } } }}void deal1(){ for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ int&lap = ll[i][j] = word[i].size() ; while( word[i].substr(word[i].size() - lap) != word[j].substr(0 , lap) ) lap -- ; } }}int DP(int i , int j){ if( dp[i][j] != -1 ) return dp[i][j] ; int jj = j ^ ( 1<<i ) ; dp[i][j] = jj ? (1<<30) : word[i].size() ; for(int ii=0;ii<n;ii++){ if( (jj&(1<<ii)) != 0 ){ dp[i][j] = MIN( dp[i][j] , DP(ii,jj) + word[i].size() - ll[i][ii] ); } } return dp[i][j] ;}void deal2(){ N = 1<<n ; memset(dp , -1, sizeof(dp)); for(int i=0;i<n;i++){ DP( i , N-1 ); } //DP(); int m = N - 1 ; ans = "" ; int prev = -1 ; while( m ){ int best = -1; int bestLen; string bestAdd; for (int f = 0; f < n; f++) if (m & (1 << f) ){ int len = dp[f][m]; string add = word[f]; if (prev >= 0) { len -= ll[prev][f]; add = add.substr(ll[prev][f]); } if (best < 0 || len < bestLen || len == bestLen && add < bestAdd) { best = f; bestLen = len; bestAdd = add; } } ans += bestAdd; prev = best; m ^= ( 1 << best ) ; } int cnt = ans.size() ; LL res = 0 ,add = 1 ; for(int i=cnt-1;i>=0;i--){ if( ans[i] == '1' ) res = ( res + add ) % Mod ; add = add * 2 % Mod ; } cout << res << endl ;}int main(){ while( cin >> n ){ init() ; move() ; deal1() ; deal2() ; } return 0 ;}
- HDU_3828 A + B problem 状态dp
- problem 1000: A+B problem
- Problem 1000 A+B Problem
- Problem - 1000 A + B Problem
- A + B Problem
- A + B Problem
- 1000 A+B Problem
- 1001 A + B Problem
- A+B Problem
- 1000 A+B Problem
- A + B Problem
- A + B Problem
- 1000 A + B Problem
- 1000 A+B Problem
- A + B Problem
- A + B Problem II
- A + B Problem II
- POJ1001 -- A-B Problem
- Android 在代码中设置ProgressBar的style
- 关于弹出框与源页面交互的一点小收获(基于wabacus开发框架)
- linux C经典面试题六
- NSOperationQueue
- oracle插入语句中带有“&”符号
- HDU_3828 A + B problem 状态dp
- 解决vim 显示乱码问题
- hdu 1213 How Many Tables
- 选择器
- SOAP 二
- MemCache基础及示例(2)
- eucalyptus 在centos下的安装
- 实现文本框只允许输入数字和退格键
- 用C++进行函数式编程