該來的總是要來的————————
經典問題,石子合並。
對於 f[i][j]= min{f[i][k]+f[k+1][j]+w[i][j]}
From 黑書
凸四邊形不等式:w[a][c]+w[b][d]<=w[b][c]+w[a][d](a<b<c<d)
區間包含關系單調: w[b][c]<=w[a][d](a<b<c<d)
定理1: 如果w同時滿足四邊形不等式和決策單調性 ,則f也滿足四邊形不等式
定理2: 若f滿足四邊形不等式,則決策s滿足 s[i][j-1]<=s[i][j]<=s[i+1][j]
定理3: w為凸當且僅當w[i][j]+w[i+1][j+1]<=w[i+1][j]+w[i][j+1]
簡要證明:
若w[a][c]+w[b][d]<=w[b][c]+w[a][d],歸納證明f[a][c]+f[b][d]<=f[b][c]+f[a][d]
設f[a][d]最優決策是在s取到,f[b][c]最優決策在t取到,設s<t,反之同理
可知a<s<t<c<d
f[a][c]+f[b][d]<=f[a][s]+f[s+1][c]+w[a][c] + f[b][t]+f[t+1][d]+w[b][d]
=f[a][s]+f[s+1][c]+w[a][d] + f[b][t]+f[t+1][d]+w[b][c]
<=f[a][s]+w[a][d]+f[s+1][d] + f[b][t]+w[b][c]+f[t+1][c] 歸納得到 sc+td<sd+tc 起始條件即定理3
=f[a][d]+f[b][c]
得證.
若f[a][c]+f[b][d]<=f[b][c]+f[a][d],則s[i][j-1]<=s[i][j]<=s[i+1][j]
僅證s[i][j-1]<=s[i][j],右邊同理
記f_k[i][j]=f[i][k]+f[k+1][j]+w[i][j]
記s點為[i,j]最優點,t點為[i,j+1]最優點,
則只需證明 在[i,j+1]決策時, 取s點能夠比取在k∈[i,s-1]的點更優即可
即證明 f_s[i,j+1]<=f_k[i,j+1]
又因為f_s[i,j]<=f_k[i,j]
只需證明 0 <= f_k[i,j] - f_s[i,j] <= f_k[i,j+1] - f_s[i,j+1]
可發現右邊即 f_k[i,j] + f_s[i,j+1] <= f_k[i,j+1] + f_s[i,j]
展開后即: f[k][j] + f[s][j+1] <= f[k][j+1] + f[s][j]
正是 k<s<j<j+1 的四邊形不等式
得證.
一般利用定理3證明凸函數,然后利用定理2的結論 s[i][j-1]<=s[i][j]<=s[i+1][j]
就能夠使得復雜度由O(n^3)降低為O(n^2)
詳細證明參見《動態規划算法的優化技巧》--毛子青(會因為論文用i,j,i',j'搞得霧水,但是慢慢推一下就能夠出來)
#include <cstdio> #include <cstring> #define N 1005 int s[N][N],f[N][N],sum[N],n; int main() { while(scanf("%d",&n)!=EOF) { memset(f,127,sizeof(f)); sum[0]=0; for(int i=1; i<=n; i++){ scanf("%d",&sum[i]); sum[i]+=sum[i-1]; f[i][i]=0; s[i][i+1]=i; } for(int i=1; i<=n; i++) f[i][i+1]=sum[i+1]-sum[i-1]; for(int i=n-2; i>=1; i--) for(int j=i+2; j<=n; j++) for(int k=s[i][j-1]; k<=s[i+1][j]; k++) if(f[i][j]>f[i][k]+f[k+1][j]+sum[j]-sum[i-1]) { f[i][j]=f[i][k]+f[k+1][j]+sum[j]-sum[i-1]; s[i][j]=k; } printf("%d\n",f[1][n]); } return 0; }
值得注意的是:若是求石子合並的最大值,則不能用四邊形不等式。可以證明 f[i,j]=max(f[i+1][j],f[i][j-1])+w[i][j]
POJ1160
f[i][j]=max(f[k][j-1]+w[k+1][i])
其中f[i][j]表示前i個村落有j個郵電局,w[i][j]表示[i,j]區間上安裝一個郵電局最短路徑和
其中w[i][j]郵電局必然是安裝在(i+j)/2(中位數)的村落中,若(i+j)/2不為整數,則中間兩個村落都可以。證明可以看《算法導論》
至於四邊形不等式,這次,可以直接容易得到 s[i][j-1]<=s[i][j]<=s[i+1][j] 稍微證明下就可以出來,憑感覺都是對的。
#include <cstdio>
#include <cstring>
#define min(x,y) (x>y?y:x)
int v,p,a[305],sum[305],w[305][305],f[305][35],s[305][35]; int main() { memset(f,127,sizeof(f)); scanf("%d%d",&v,&p); for(int i=1; i<=v; i++){ scanf("%d",&a[i]); sum[i]=a[i]+sum[i-1]; } for(int i=1; i<=v; i++){ w[i][i]=0; for(int j=i+1; j<=v; j++){ w[i][j]=sum[j]-sum[(i+j)/2]-sum[(i+j)/2-1]+sum[i-1]; if((i+j)%2!=0) w[i][j]-=a[(i+j)/2]; } } for(int i=1; i<=v; i++) f[i][1]=w[1][i]; for(int j=2; j<=p; j++){ s[v+1][j]=v-1; for(int i=v; i>=j; i--) { for(int k=s[i][j-1]; k<=s[i+1][j]; k++) if(f[i][j]>f[k][j-1]+w[k+1][i]) { f[i][j]=f[k][j-1]+w[k+1][i]; s[i][j]=k; } } } printf("%d",f[v][p]); return 0; }