问题描述:
给定图像的灰度值序列
其中
为第i个像素的灰度值,采用变位压缩存储格式,如何对P进行分段,以使得存储P占用的二进制位数达到最少?
问题要求
图像压缩问题就是要我们找出一个最优的分段方案,使得总存储空间最小。
问题分析
一幅图像的由很多个像素点构成,像素点越多分辨率越高,像素的灰度值范围为0~255,也就是需要8bit来存储一个像素的灰度值信息。一幅由n*m像素点构成的图像,所需存储空间大小为:n*m*8bit=8nmbit。然而,正常情况下,一幅图像的某一范围内像素点的灰度值是很接近的,表现为一幅图片某一区域颜色相近。
实现思路
子问题的划分边界问题: 设像素序列x1,x2,x3。先把它分为两段,尾段和前段。我们假设尾端是没有分割的。并且前段是已经被分割成了最优解。则我们只需要遍历尾段的长度,就可以得到整个序列的最优解。也就是
(1)假设最后一段只有一个元素x3,用x3的长度+一个段头+min(x1,x2)
(2)假设尾段(X2,x3),加上min(x1);
(1)(2)两个结果比较,就可以拿到最小值,而min(x1,x2)就是子问题
所以递推公式是dp[i] = dp[i - j] + j x log(max(Pi-j+1,…,Pi)) + 11
初始化:
l[i]:i段像素总个数;
b[i]:每个像素占的比特空间;
bmax一段中占用比特空间最多的像素占用的空间;
s[i]:每个段占用的总比特空间
约束条件:
每段像素个数l[i]<=256;
i段占用空间:b[i]*l[i]+11
状态表示:
l[i]表示以P[i](包含)结尾的最优分段所需的bit数(最少)
状态转移:
l[i] =P[i-k]+b[i-k+1,i]+11 , 0<=k<=i
b[i-k+1,i]为求i-k+1到i这个序列每个像素灰度值需要的的bit数
其中,k<=256(图像压缩的限制)
l[i] =P[i-k]+b[i-k+1,i]+11 , 0<=k<=min(i,256)
以下两个实例帮助理解


完整代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<iostream>
#include<fstream>
using namespace std;
const int N = 1e5 + 10;
int P[N];//存储每个像素的灰度值
int length(int i)//计算每个像素的灰度值占的bit
{
int k = 1;
i = i / 2;
while (i > 0) {
k++;
i = i / 2;
}
return k;
}
void compress(int* p, int n, int* s, int* I, int* b)
{
int header = 11;//一个段需要3位来存储每个像素点需要几个比特来存储,8位来存储这个像素段有多少个像素点。
int Imax = 256;
s[0] = 0;//定义s[0]
for (int i = 1; i <= n; i++) {
b[i] = length(p[i]);
int bmax = b[i];//bmax是最后一段中占用比特最多的像素需要几个比特的空间来存储,它初始化为最后一个点需要的空间
s[i] = s[i - 1] + bmax;//s[i]记录前段,即1-i像素所需的最小空间,它初始化为前s[i-1]加上最后一段(只有一个点组成)
I[i] = 1;
for (int k = 2; k <= i && k <= Imax; k++)
{//k是最后一段大小,注意i受约束条件限制<=256
if (bmax < b[i - k + 1])//更新每段像素点占的最大比特空间
bmax = b[i - k + 1];
if (s[i] > s[i - k] + k * bmax)
{
s[i] = s[i - k] + k * bmax;
I[i] = k;
}
}
s[i] += header;//最后加上段头的占用的空间
}
}
int main() {
ifstream OpenFile("input.txt");
int n;
while (OpenFile >> n) {//用打开的文件流对象来读取输入数据,输入总的像素点个数
cout << n << endl;
for (int i = 1; i <= n; i++) {//将每个像素的灰度值依次存放到P数组中
OpenFile >> P[i];
cout << P[i] << "\t";
}
}
cout << endl;
OpenFile.close(); //关闭文件流对象
int* s = new int[n + 1]();
int* I = new int[n + 1]();
int* b = new int[n + 1]();
compress(P, n, s, I, b);
int t = n;
vector<int>v;
while (t) {
v.push_back(I[t]);//将长度信息用vector存储
t = t - I[t];
}
reverse(v.begin(), v.end());//倒置
printf("最少需要:%dbit\n", s[n]);
for (int i = 0, t = 1; i < v.size(); i++) {//输出分段信息
for (int j = 0; j < v[i]; j++)
printf("%d ", P[t++]);
printf("长%d,每个像素需要%dbit\n", I[t - 1], b[t-1]);
}
return 0;
}
文件输入("input.txt")

运行结果

时间复杂度分析:
因为我们规定一段的像素个数最多是256个,所以k<=256。虽然看是两层循环,但是内层循环是一个常数级,因为最大只能到256;而外层循环是n个。所以时间复杂度就是O(n)。

1575

被折叠的 条评论
为什么被折叠?



