[dp] Jzoj P6012 荷马史诗
题解
- 假设,我们把每个s打进一棵Trie里,把0当成边权为1,把1当成边权为2,那么答案其实就是要使∑w[i]*dep[i]最小
- 那么要使答案越小,那么显然就是把出现次数多的,也就是w[i]大的先打进Trie里,那么就先将w从大到小排序
- 对于Trie树上的点,要不就是叶子节点,要不就有两个儿子,一个延申一层,一个延伸两层
- 那么这样的话,我们在DP里只用知道当前层的状态和上一层的状态就行了
- 设f[i][j][k]为当前在第i层,上一层有j个空叶子,当前层有k个空叶子的最小答案
- 有两种转移:①把当前层填入上一层的某个叶子f[i+1][j-1][k]=min(f[i-1][j][k]) ②把上一层的j个叶子扩展,左右都拓展j个,左拓展深度1,右拓展深度2,那么上一层就有j+k个叶子,当前层就有j个叶子 f[i][j+k][k]=min(f[i][j][k]+w[n-i])
- 然后我们可以发现一个问题,如果这样存的话f数组的大小就到了N^3,这样显然会炸的飞起
- 再返回去看DP,f[i][j][k]显然只会转移到下一层,简单点说也就是i只会转移到i+1,这样的话就可以用滚动数组来做
- 时间复杂度的话,O(n^3)???(大佬貌似说跑不满)
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #define ll long long 6 using namespace std; 7 const ll N=1e3+10,inf=0x7fffffff; 8 ll f[3][N][N],a[N],n,l,r; 9 int main() 10 { 11 freopen("b.in","r",stdin),freopen("b.out","w",stdout),scanf("%lld",&n); 12 for (ll i=1;i<=n;i++) scanf("%lld",&a[i]); 13 for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) f[0][i][j]=inf; 14 sort(a+1,a+n+1),f[0][1][0]=0; 15 for (ll i=2;i<=n;i++) a[i]+=a[i-1]; 16 for (ll i=0;i<n;i++) 17 { 18 l=i&1,r=l^1; 19 for (ll j=0;j<=n-i;j++) for (ll k=0;k<=n-i-j;k++) f[r][j][k]=inf; 20 for (ll j=0;j<=n-i;j++) 21 for (ll k=0;k<=n-i-j;k++) 22 if (f[l][j][k]!=inf) 23 { 24 if (j) f[r][j-1][k]=min(f[r][j-1][k],f[l][j][k]); 25 if (j+k) f[l][j+k][j]=min(f[l][j+k][j],f[l][j][k]+a[n-i]); 26 } 27 } 28 printf("%lld",f[r][0][0]); 29 }