石子合並及優化
阿新 • • 發佈:2019-05-02
div sans col ria 都是 read noi 表示 detail
1.石子歸並
非常樸素,順著推即可
w [ i ] [ j ] 表示把第i堆到第j堆的石子和到一起的最後一步的代價
f [ i ] [ j ] = min{f [ i ] [ k ] + f [ k+1 ] [ j ] + w[ i ] [ j ] | i <= k < j , i <= j}
for(int i=1;i<=n;++i)//長度
for(int j=1;j+i<=n+1;++j)//起點
{
int e=j+i-1;
for(int k=j;k<e;++k)//分割點
{
dp1[j][e] =min(dp1[j][k]+dp1[k+1][e]+sum[e]-sum[j-1],dp1[j][e]);
}
}
2.[NOI1995]石子合並
在上面那個問題略微變動一下,變成了環形,可以將其暴力拆成鏈
void read()
{
memset(dp1,0x3f,sizeof(dp1));
red(n);
for(int i=1;i<=n;++i)
{
red(a[i]);
a[i+n]=a[i];
}
}
void work()
{
for(int i=1;i<=2*n;++i)
{
sum[i]=sum[i-1]+a[i];
dp1[i][i]=0;
}
for(int i=1;i<=n;++i)
for(int j=1;j+i<2*n;++j)
{
int e=j+i-1;
for(int k=j;k<e;++k)
{
dp1[j][e]=min(dp1[j][k]+dp1[k+1][e]+sum[e]-sum[j-1 ],dp1[j][e]);
dp2[j][e]=max(dp2[j][k]+dp2[k+1][e]+sum[e]-sum[j-1],dp2[j][e]);
}
}
minn=INF;
for(int i=1;i<=n;++i)
{
minn=min(minn,dp1[i][i+n-1]);
maxx=max(maxx,dp2[i][i+n-1]);
}
printf("%d\n%d",minn,maxx);
}
3.四邊形優化
上面的樸素寫法復雜度都是O(n^3),有沒有更好的寫法嗎?
有,利用數學裏的四邊形不等式
f[a][c]+f[b][d]<=f[b][c]+f[a][d]
交叉小於包含,即交叉的兩個區間,a到c和b到d的值滿足小於等於包含的兩個區間[bc包含於ad])
則說這個東西滿足四邊形不等式
簡而言之,就是該區間的最優分割點一定在前一個區間和後一個區間之間,即:
s [ i ] [ j - 1 ] <= s [ i ] [ j ] <= s [ i + 1 ] [ j ]
for(int i=1;i<=n;++i)
for(int j=1;j+i<2*n;++j)
{
int e=j+i-1;
for(int k=r[j][e-1];k<=r[j+1][e];++k)
{
if(dp1[j][e]>dp1[j][k]+dp1[k+1][e]+sum[e]-sum[j-1])
{
dp1[j][e]=dp1[j][k]+dp1[k+1][e]+sum[e]-sum[j-1];
r[j][e]=k;
}
}
}
石子合並及優化