做题感悟:这题很经典 ,需要模拟一下找规律,还是那句话遇到题自己应该手动推一下。
解题思路:
这题如果手动推几组数据的话就应该发现 ,如果放进队列的元素是递减的话,这样才可以连续合并,如果队列中有 a ,b , a < b 那么 a 前面的必定不会与 b 经过合并再合并,因为越合并越大,so ~> 队列中最多才存 12 个数,可以用状态压缩压缩一下。注意要用滚动数组,不用可能超时。
代码:
#include<iostream> #include<sstream> #include<map> #include<cmath> #include<fstream> #include<queue> #include<vector> #include<sstream> #include<cstring> #include<cstdio> #include<stack> #include<bitset> #include<ctime> #include<string> #include<cctype> #include<iomanip> #include<algorithm> using namespace std ; #define INT __int64 #define L(x) (x * 2) #define R(x) (x * 2 + 1) const int INF = 0x3f3f3f3f ; const double esp = 0.0000000001 ; const double PI = acos(-1.0) ; const int mod = 1000000007 ; const int MY = (1<<13) + 5 ; const int MX = 500 + 5 ; const int MS = 13 ; int n ; int g[MX] ,dp[2][MY] ; int main() { int Tx ; scanf("%d" ,&Tx) ; while(Tx--) { scanf("%d" ,&n) ; for(int i = 1 ;i <= n ; ++i) scanf("%d" ,&g[i]) ; memset(dp ,-1 ,sizeof(dp)) ; dp[0][0] = 0 ; for(int i = 1 ;i <= n ; ++i) for(int S = 0 ;S <= MY ; ++S) if(dp[(i-1)%2][S] != -1) // 由前一行的合法状态递推第 i 行 { dp[i%2][S] = max(dp[i%2][S] ,dp[(i-1)%2][S]) ; // 不拿 int k ,key ,sum ,ret ,temp ; if(S) { for(int k = 0 ;k <= 12 ; ++k) // 计算队列中的最小的元素 if(S&(1<<k)) { temp = (1<<k) ; break ; } } else temp = 0 ; if(temp < g[i]) // 无法合并 dp[i%2][g[i]] = max(dp[i%2][g[i]] ,dp[(i-1)%2][S] + g[i]) ; else if(temp == g[i]) // 可以合并 { key = S ; sum = g[i] ; ret = g[i] ; while(key&ret) { sum += ret*2 ; key = key - ret ; ret <<= 1 ; } dp[i%2][key+ret] = max(dp[i%2][key+ret] ,dp[(i-1)%2][S] + sum) ; } else if(temp > g[i]) // 放入队列 dp[i%2][g[i] + S] = max(dp[i%2][g[i] + S] ,dp[(i-1)%2][S] + g[i]) ; } int ans = 0 ; for(int S = 0 ;S <= MY ; ++S) ans = max(ans ,dp[n%2][S]) ; printf("%d\n" ,ans) ; } return 0 ; }
时间: 2024-12-23 21:25:48