首先給個鏈接:視頻中老師講的特別好:https://www.bilibili.com/video/BV1C7411X7RD?from=search&seid=16284014602967026759(通俗易懂)
1、問題描述:
在計算機中,常用像素點的灰度值序列{p1,p1,……pn}表示圖像。其中整數pi,1<=i<=n,表示像素點i的灰度值。通常灰度值的范圍是0~255。因此最多需要8位表示一個像素。
壓縮的原理就是把序列{p1,p1,……pn}進行設斷點,將其分割成一段一段的。分段的過程就是要找出斷點,讓一段里面的像素的最大灰度值比較小,那么這一段像素(本來需要8位)就可以用較少的位(比如7位)來表示,從而減少存儲空間。
b代表bits,l代表length,分段是,b[i]表示每段一個像素點需要的最少存儲空間(少於8位才有意義),l[i]表示每段里面有多少個像素點,s[i]表示從0到i壓縮為一共占多少存儲空間。
如果限制l[i]<=255,則需要8位來表示l[i]。而b[i]<=8,需要3位表示b[i]。所以每段所需的存儲空間為l[i]*b[i]+11位。假設將原圖像分成m段,那么需要位的存儲空間。
圖像壓縮問題就是要確定像素序列{p1,p1,……pn}的最優分段,使得依此分段所需的存儲空間最小。
2、最優子結構性質
設l[i],b[i],1<=i<=m是{p1,p1,……pn}的一個最優分段,則l[1],b[1]是{p1,……,pl[1]}的一個最優分段,且l[i],b[i],2<=i<=m是{pl[1]+1,……,pn}的一個最優分段。即圖像壓縮問題滿足最優子結構性質。
3、遞推關系
設s[i],1<=i<=n是像素序列{p1,p1,……pi}的最優分段所需的存儲位數,則s[i]為前i-k個的存儲位數加上后k個的存儲空間。由最優子結構性質可得:(圖解如下)
,式中
或者參考北大屈教授視頻如下:
4、構造最優解
數組l[i],b[i]記錄了最優分段所需的信息最優分段的最后一段的段長度和像素位數分別存儲在l[n]和b[n]中,其前一段的段長度和像素位數存儲於l[n-l[n]]和b[n-l[n]]中,依此類推,可在O(n)時間內構造最優解。
算法具體實現代碼如下:

//動態規划 圖像壓縮問題 #include "stdafx.h" #include <iostream> using namespace std; const int N = 7; int length(int i); void Compress(int n,int p[],int s[],int l[],int b[]); void Tracebace(int n,int& i,int s[],int l[]); void Output(int s[],int l[],int b[],int n); int main() { int p[] = {0,10,12,15,255,1,2};//圖像灰度數組 下標從1開始計數 int s[N],l[N],b[N]; cout<<"圖像的灰度序列為:"<<endl; for(int i=1;i<N;i++) { cout<<p[i]<<" "; } cout<<endl; Compress(N-1,p,s,l,b); Output(s,l,b,N-1); return 0; } void Compress(int n,int p[],int s[],int l[],int b[]) { int Lmax = 256,header = 11; s[0] = 0; for(int i=1; i<=n; i++) { b[i] = length(p[i]);//計算像素點p需要的存儲位數 int bmax = b[i]; s[i] = s[i-1] + bmax; l[i] = 1; for(int j=2; j<=i && j<=Lmax;j++) { if(bmax<b[i-j+1]) { bmax = b[i-j+1]; } if(s[i]>s[i-j]+j*bmax) { s[i] = s[i-j] + j*bmax; l[i] = j; } } s[i] += header; } } int length(int i) { int k=1; i = i/2; while(i>0) { k++; i=i/2; } return k; } void Traceback(int n,int& i,int s[],int l[]) { if(n==0) return; Traceback(n-l[n],i,s,l); s[i++]=n-l[n];//重新為s[]數組賦值,用來存儲分段位置 } void Output(int s[],int l[],int b[],int n) { //在輸出s[n]存儲位數后,s[]數組則被重新賦值,用來存儲分段的位置 cout<<"圖像壓縮后的最小空間為:"<<s[n]<<endl; int m = 0; Traceback(n,m,s,l); s[m] = n; cout<<"將原灰度序列分成"<<m<<"段序列段"<<endl; for(int j=1; j<=m; j++) { l[j] = l[s[j]]; b[j] = b[s[j]]; } for(int j=1; j<=m; j++) { cout<<"段長度:"<<l[j]<<",所需存儲位數:"<<b[j]<<endl; } }
算法Compress只需O(n)空間。由於在算法Compress中j的循環次數不超過256,故對每一個確定的i可在O(1)時間內完成。因此整個算法的時間復雜度為O(n)。算法Compress的執行過程可以下圖表示:
方法Output中,在輸出s[n]的最小存儲空間后,s[]數組被重新賦值,用來存儲分段的位置,一邊回溯構造最優解。程序運行結果如下:
參考文獻:王曉東《算法設計與分析》
CSDN:https://blog.csdn.net/liufeng_king/article/details/8648195