洛谷P3147 [USACO16OPEN]262144 2048 合并 倍增 动归 递推
洛谷P3147 [USACO16OPEN]262144
2048 合并 
题意 每次可以把相邻的两个相同的数字合并,  如x 和 x 合并之后  值就是 x+1 
求最终最大能够合并到的数字大小    
题解  一种 做法类似倍增  动归 递推 
f[ i ][ j ]  表示 从第 i -- f[ i ][ j ]-1 位  这几位的和是  j     
然后就可以递推 或者说动归了,因为要相同j是由前后两个相同 j-1合并而来的, 
f[ i ][ j ] = f[ f[ i ][ j-1 ] ][ j-1 ]   合并当然是从小到大合并的 
因为先把小的合并了,大的合并机会才会更多嘛,另一个理由就是根据公式,
在算 j 时 需要事先算出 i-1 才行
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <algorithm> 6 #include <string> 7 #include <iostream> 8 #include <iomanip> 9 using namespace std ; 10 11 const int maxn = 300011,maxm = 61; 12 13 int n,ans ; 14 int a[maxn],f[maxn][maxm-1] ; 15 16 inline int read() 17 { 18 char ch = getchar() ; 19 int x = 0,f = 1 ; 20 while(ch<'0'||ch>'9') { if(ch=='-') f = -1 ; ch = getchar() ; } 21 while(ch>='0'&&ch<='9') { x = x*10 + ch-48 ; ch = getchar() ; } 22 return x*f ; 23 } 24 25 int main() 26 { 27 n = read() ; 28 for(int i=1;i<=n;i++ ) a[ i ] = read() ; 29 for(int j=1;j<maxm;j++) 30 { 31 for(int i=1;i<=n;i++) 32 { 33 if(a[ i ]==j) f[ i ][ j ] = i+1 ; 34 else f[ i ][ j ] = f[ f[ i ][ j-1 ] ][j-1] ; 35 if(f[ i ][ j ]) ans=max(ans,j) ; 36 } 37 } 38 printf("%d\n",ans) ; 39 return 0 ; 40 }
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号