【日记】6.14
几天做了几道状态压缩的题目,然后下午考试竟然真的让我碰到了、
①第一道题,是一个字母树的问题,我没有写,因为在插入后的查找构建N次字母树就超时了,但是我写了个最简单的搜索的,目测能过50%但是提交的时候晚了1分钟……于是就没有这道题的成绩了,50分啊( ⊙ o ⊙ )
最后知道,只要把读入的数据线排序,在查询的时候插入就好了……这么简单竟然没有想到。
②刚看的状态压缩,就遇到这种题让我情何以堪,果断状态压缩搞之,把每一列的状态压缩成一个数字,那么就应该是0~15,不难得到状态转移方程:
f[i+1][j]=∑f[i][k] f[i][j]表示当前第i行状态为j的方法数,枚举每一行然后dfs就可以完成状态转移了,这个时间复杂度应该是O(K*N)的,k是个常数,具体不知道,因为dfs的时间复杂度不怎么会算。
明显是只能拿70%的,结果确实只有70分,但是我们可以知道,这是一个递推的式子,就可以试着用矩阵乘法优化,那么构造一个矩阵A,为16*16,对角线为1,其余为0,连乘N次后再和f[1]这行相乘,就能得到f[N+1],最后的答案就是f[n+1][0],而矩阵的N次方很明显可以用矩阵乘法优化。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 *Prob : baga 3 *Data : 2012-6-14 4 *Sol : 状态压缩递推 5 *Author : Zhou Hang 6 */ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <algorithm> 11 12 #define MaxT 16 13 #define oo 997 14 #define MaxN 1000000 15 16 17 using namespace std; 18 19 int n,i,j; 20 int f[MaxN][MaxT]; 21 22 //k表示在第k个格子,s表示当前铺砖状态,news表示下一层被上一层所影响的状态 23 void Dfs(int k,int s,int news) 24 { 25 if (k==4) 26 { 27 f[i+1][news]=(f[i+1][news]+f[i][j])%oo; 28 return; 29 } 30 //当前位置已经被铺就跳下一格 31 if ( s&(1<<k) ) 32 Dfs(k+1,s,news); 33 else { 34 //竖铺 35 Dfs(k+1,s|(1<<k),news|(1<<k)); 36 //横铺 37 if ( ((s&(1<<(k+1)))==0 ) && (k+1<4) ) 38 Dfs(k+2,((s|(1<<k)))|((1<<(k+1)) ),news); 39 } 40 } 41 42 int main() 43 { 44 freopen("baga.in","r",stdin); 45 freopen("baga.out","w",stdout); 46 47 scanf("%d",&n); 48 49 f[1][0]=1; 50 for (i=1; i<=n; i++) 51 { 52 for (j=0; j<=15; j++) 53 if (f[i][j]>0) Dfs(0,j,0); 54 } 55 56 printf("%d\n",f[n+1][0]%oo); 57 58 fclose(stdin); fclose(stdout); 59 return 0; 60 }
③很明显的1D1D动态规划,先将工作的难度排序,那么安排给每个人的工作必须是连续的,这样才能保证总酬劳最小
f[i]=min(f[j]+w[j+1,i]) 相当于枚举最后一个人做的什么工作,这个算法的时间复杂度是O(n^2)的,10000的数据勉强能过
其实进一步想,这个递推公式中的w其实是满足凸性的(具体看四边形不等式),很简单就可以证明,那么就告诉了我们,这个决策是单调的,那么f[i]的决策可以从f[i-1]的最优决策开始向后枚举,或者用斜率优化都基本上可以优化到O(n)级别的。但是我貌似优化错了……最裸的也写错了,虽然分段了还是没有拿到分数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 *Prob : dividea 3 *Data : 2012-6-14 4 *Sol : 动归+决策单调性 5 */ 6 7 #include<iostream> 8 #include<stdio.h> 9 #include<cstring> 10 #include<algorithm> 11 using namespace std; 12 13 int n,K,C; 14 long long f[1000005]; 15 long long a[1000005]; 16 17 int cmp(const void *a,const void *b) 18 { 19 if(*(long long *)a>*(long long *)b) return 1; 20 return -1; 21 } 22 23 void work() 24 { 25 int i,j,last,noww; 26 memset(f,0x7f,sizeof(f)); 27 f[0]=0; 28 last=0; 29 for(i=K; i<=n; i++) 30 { 31 noww=last; 32 for(j=last; j<=i-K; j++) 33 if(f[i]>=f[j]+(a[i]-a[j+1])*(a[i]-a[j+1])+C) 34 { 35 f[i]=f[j]+(a[i]-a[j+1])*(a[i]-a[j+1])+C; 36 noww=j; 37 } 38 last=noww; 39 } 40 } 41 42 void print() 43 { 44 45 } 46 47 int main() 48 { 49 50 freopen("dividea.in","r",stdin); 51 freopen("dividea.out","w",stdout); 52 53 scanf("%d%d%d\n",&n,&K,&C); 54 int i; 55 for(i=1; i<=n; i++) 56 scanf("%d",&a[i]); 57 qsort(a+1,n,sizeof(a[1]),cmp); 58 59 work(); 60 61 printf("%I64d\n",f[n]); 62 63 fclose(stdin); fclose(stdout); 64 return 0; 65 }
这次的考试失误太多了,比如第一题没有交上去,为了提醒自己就没有去补交了,还有最后一题的优化也写错了,理论上是最裸的就过了,事实也是如此。
理论上这如果真是考试的话,预计得分应该是220左右。需要复习下矩阵乘法了,这也是下个星期的目标之一。