#include <stdio.h>
#include <math.h>

#define N 9  // 定义数组大小为9

// 计算一个整数在二进制中需要的位数
int length(int i) {   
    int k = 1;  // 初始化位数计数器
    i = i / 2;  // 将整数除以2，开始计算
    while (i > 0) {  // 当i大于0时继续
        k++;  // 每次循环位数增加1
        i = i / 2;  // 继续除以2
    }  
    return k;  // 返回计算得到的位数
}
 
void Compress(int n, int p[], int s[], int l[], int b[]) {  
    int Lmax = 256, header = 11;  // 定义最大段数和头部长度
    s[0] = 0;  // 初始化s数组  
    for (int i = 0; i <= n; i++) { // 遍历每个元素 
        b[i] = length(p[i]);  // 计算当前元素的位数
        int bmax = b[i];  // 记录当前最大位数
        s[i] = (i > 0 ? s[i - 1] : 0) + bmax + header;  //当前段的最小存储长度
        l[i] = 1;  // 初始段数为1  
        for (int j = 2; j <= i + 1 && j <= Lmax; j++) {  // 遍历可能的段数 
            if (bmax < length(p[i - j + 1])) { // 更新最大位数  
                bmax = length(p[i - j + 1]);  
            }  
            // 判断是否需要更新存储长度
            if (s[i] > s[i - j] + j * bmax + header) {  
                s[i] = s[i - j] + j * bmax + header;  // 更新最小存储长度
                l[i] = j;  // 更新段数
                b[i] = bmax;  // 更新位数
            }  
        }  
        printf("组 %d: L = %d, B = %d, S = %d\n", i, l[i], b[i], s[i]);
    }  
}  

// 追溯函数，恢复最优分段信息
int TraceBack(int n, int l[], int b[]) {  
    int i = 0;
    int temp_l[N], temp_b[N];  // 临时数组存储分段信息

    // 从最后一个元素向前追溯
    while (n >= 0) {
        temp_l[i] = l[n];  // 记录段数
        temp_b[i] = b[n];  // 记录位数
        n = n - l[n];  // 更新n，向前移动
        i++;  // 增加索引
    } 

    // 反转数组，恢复正确顺序
    for (int j = 0; j < i; j++) {
        b[j] = temp_b[i - j - 1];  // 反向赋值位数
        l[j] = temp_l[i - j - 1];  // 反向赋值段数
    }
    return i;  // 返回段数
}

// 输出函数，展示最终结果
void Out(int m, int min_len, int l[], int b[], int p[]) {
    printf("最小长度：%d\n", min_len);  // 输出最小存储长度
    printf("共分成：%d段\n", m);  // 输出段数
    int index = 0;  // 初始化索引
    for (int i = 0; i < m; i++) {
        // 输出每段的信息
        printf("第%d段含有%d元素. 需要存储位数%d，", i + 1, l[i], b[i]);
        printf("元素值：");
        // 输出当前段的元素值
        for (int j = 0; j < l[i]; j++) {
            printf("%d ", p[index++]);  // 逐个输出元素
        }
        printf("\n");  // 换行
    }
}

// 主函数，程序入口
int main() {  
    int p[] = {4, 6,5,  7, 129, 138, 1,2,3};  // 输入的灰度序列
    int s[N] = {0}, l[N] = {0}, b[N] = {0};  // 初始化存储长度、段数和位数的数组

    // 输出输入序列
    printf("图像的灰度序列为：\n");  
    for (int i = 0; i < N; i++) {  
        printf("%d ", p[i]);  
    }  
    printf("\n");    

    // 调用压缩函数
    Compress(N - 1, p, s, l, b);  
    int m = TraceBack(N - 1, l, b);  // 调用追溯函数获取段数
    Out(m, s[N - 1], l, b, p);  // 输出结果
    return 0;  
}  
