1>生日蛋糕
做一个蛋糕,规定体积为n(pai),层数为n,
m,n,ri,hi均为整数
且每一层ri大于上一次rj,每一层高度hi大于hj
求最大的s表
先列方程:
V=n=∑ri*ri*hi
S=rm*rm +∑2*ri*hi
(1<=ri<=m)
可以看出,这里有明显的枚举,写dfs的特征,
枚举层数,记录上次的r,h,和直到上一层的s,v,
直到最后一层,与V比较,等则更新ans
但是n<=20000,
最大的r是132,最大的h是20000,
过大,枚举过程肯定要剪枝,
剪枝分为
1)可行性剪枝(减v)
2)最优性剪枝(减s)
a.加上估值函数mn_v,mn_s,
分别表示前i层,半径1-i枚举,高度1-i枚举的值,
这样就为搜索过程提供了一个:可行下界
以后的s和v不会过大,
(在dfs前递推预处理)
b.感觉这个剪枝不太给力,
那么我们就再加入一点,减去更多的枝
看看这个v,
似乎还可以设置一个可行上界?!
就是每次的ri*ri*hi*res +v <V
不过这个因为和ri和hi的具体值有关(ri,hi不确定),所以放在枚举中较好
那么再看看mn_s,似乎存在着一个问题:
mn_s和mn_v不太贴合,这个mn_s太理想
如果我们去贴合mn_v这个量,就要加大一层的r或h,那s与mn_s就差的多了,
最后就算与ans比较,成功更新的概率不高,
所以我们想啊想,想着再换一种思路求s的界限
看看式子:s是从侧面来的,
将s与V建立联系,就可以让s与ans更接近,不会出现中间突然的偏差,
发现:
vi=ri*ri*hi = ri*hi*2 (/2)(*ri) =si
则:resv=(n-v)
S<= s+resv/rm*2
所以完成:
#include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m; int inf=99999999; int mn_s[20],mn_v[20]; int ans; int rd; void dfs(int pos,int s,int v,int mr,int mh) { if(pos==0) { if(v==n) ans=min(ans,s); return ; } if(!mh || !mr) return ; if(s+ mn_s[pos] >ans) return ; if(v+ mn_v[pos] >n) return ; if(s+ (n-v)/mr *2 >ans) return ; for(int i=mr;i;i--) { if(pos==m) s=i*i; int mx_h=min(mh, (n-v-mn_v[pos-1])/(i*i)); //printf("%d %d %d %d\n",pos,s,v,mx_h); for(int j=mx_h;j>=pos;j--) dfs(pos-1,s+2*i*j,v+i*i*j,i-1,j-1); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++)//枚举的是层数,表示每层的最小s,v mn_v[i]=mn_v[i-1] +i*i*i, mn_s[i]=mn_s[i-1] +i*i*2;//这里的mn_s不包括底面 ans=inf; dfs(m,0,0,sqrt(n),n); if(ans==inf) printf("0\n"); else printf("%d\n",ans); return 0; }
2>addition chain
一道不容易想,怎么剪枝的题
然后居然是迭代加深...
震惊,然后因为层数不多,我用的是优先队列去重,减少无用递归
#include<cstdio> #include<cstdlib> #include<algorithm> #include<queue> using namespace std; int n,maxd; int d[10003],t[10003]; bool dfs(int pos) { if(pos>maxd) return false; int nw; priority_queue <int> q; for(int i=pos-1;i;i--) for(int j=i;j;j--) { nw=d[i]+d[j] ; if(pos<maxd ) { if((nw<<(maxd-pos))<n) break; //这里其实还可以多加一个可行性剪枝:计算之后可以到达的最大结果 if(nw<n && nw>d[pos-1]) q.push(nw);//注意这里还要求单调递增 } else if(nw==n) { for(int i=1;i<maxd;i++) printf("%d ",d[i]); printf("%d\n",n); return true; } } bool ok=false; while(!q.empty() && !ok) { nw=q.top(); q.pop(); while(!q.empty() && q.top()==nw) q.pop(); d[pos]=nw; ok=dfs(pos+1); } return ok; } int main() { d[1]=1,d[2]=2; while(~scanf("%d",&n) && n ) if(n<4) { for(int i=1;i<=n;i++) printf("%d ",i); printf("\n"); } else { for(maxd=3; ;maxd++)if(dfs(3)) break; } return 0; }
3>打开灯泡