首页 > 代码库 > 【Foreign】咏叹 [模拟退火]

【Foreign】咏叹 [模拟退火]

咏叹

Time Limit: 100 Sec  Memory Limit: 256 MB

Description

  有n根木棍,第i根长度为ai。你要贴着墙围出一个矩形区域,木棍围成的矩形边缘必须平行或垂直于墙,且又一边必须利用墙。你可以把至多1根木棍劈成两根(不一定要在整数位置)。最大化矩形面积。

Input

  包含多组数据。每组数据第一行一个整数n,第二行n个整数ai。

Output

  输出面积最大的矩形与墙壁平行的那条边的长度(显然是一个整数),若有多个最优解输出与墙壁平行的那条边最长的。

Sample Input

    3
    3 3 3
    4
    4 4 4 4

Sample Output

  6
  8

HINT

  对于10%的数据,n=2。
  对于30%的数据,n<=15。
  对于50%的数据,n<=32。
  对于另外20%的数据,ai<=100。
  对于100%的数据,2<=n<=40,1<=ai<=10^9,数据不超过10组。

Source

  首先,必然是全部木棍都用上的时候最优,对于n=2的时候,显然就是分三种情况讨论一下就好了。

  然后我们从n=2的情况拓展。发现,其实可以把多个木棍并在一起,使其变为n=2的情况,然后讨论。那么现在答案只和两段的长度有关了。

  但是直接暴力搜索是O(2^40)的,显然不行,我们考虑分为两部分来搜索,搜索前n/2个,和后n/2个,表示选不选得到的价值,现在效率是O(2*2^20)。

  然后怎么得到答案呢?显然:如果我们设宽为x,则长为tot-2x(tot为总长),那么这是一个二次函数,必然有峰值。

  所以我们大胆猜测,我们确定了一半,另外一半使得其答案最优的话也可能满足有峰值的性质

  然后我们固定一半,另一半运用模拟退火求解即可!

Code

技术分享
  1 #include<iostream>    2 #include<string>    3 #include<algorithm>    4 #include<cstdio>    5 #include<cstring>    6 #include<cstdlib>    7 #include<cmath>  8 #include<ctime>  9 using namespace std;  10 typedef long long s64; 11  12 const int ONE = 2100000; 13  14 int T,n; 15 int a[45],top1,top2; 16 s64 Stk1[ONE],Stk2[ONE]; 17 s64 Square, Ans, tot, RE; 18  19 int get()  20 { 21         int res=1,Q=1;  char c; 22         while( (c=getchar())<48 || c>57) 23         if(c==-)Q=-1; 24         if(Q) res=c-48;  25         while((c=getchar())>=48 && c<=57)  26         res=res*10+c-48;  27         return res*Q;  28 } 29  30 s64 Get(s64 width,s64 length) 31 { 32         s64 res = length * width; 33          34         if(res > Square || (res==Square && length>Ans)) 35             Square = res, Ans = length; 36         return res; 37 } 38  39 s64 Check(s64 A,s64 B) 40 { 41         if(A > B) swap(A,B); 42         s64 res = 0; 43         res = max(res,Get(A, B-A)); 44         res = max(res,Get(A/2,B)); 45         res = max(res,Get(B/2,A)); 46         return res; 47 } 48  49 void Dfs1(s64 val,int T) 50 { 51         if(T>n/2) {Stk1[++top1] = val; return;} 52         Dfs1(val,T+1);    Dfs1(val+a[T],T+1); 53 } 54  55 void Dfs2(s64 val,int T) 56 { 57         if(T>n) {Stk2[++top2] = val; return;} 58         Dfs2(val,T+1);    Dfs2(val+a[T],T+1); 59 } 60  61 s64 Judge(int i,int j) 62 { 63         return Check(Stk1[j] + Stk2[i],tot - Stk1[j] -Stk2[i]); 64 } 65  66 double Random() {return (double)rand()/RAND_MAX;} 67 void Deal(int id) 68 { 69         int Now = top2/2; 70         double Temper = top2/3; 71         while(Temper >= 1) 72         { 73             int A = Now + (int)Temper * (Random()*2.0-1); 74             if(A<=0) A = (int)Temper * Random() + 1; 75             s64 dE = Judge(A,id) - Judge(Now,id); 76             if(dE > 0 || Random()<=exp(dE))  77                 Now = A; 78             if(Temper > top2 / 2) Temper *= 0.1; 79             Temper *= 0.75; 80         } 81         Judge(Now-1,id);    Judge(Now+1,id); 82 } 83  84 void Solve2() 85 { 86         top1 = top2 = tot = 0; 87         for(int i=1;i<=n;i++) a[i]=get(),a[i]*=2,tot += a[i];  88         Dfs1(0,1);        sort(Stk1+1,Stk1+top1+1);    top1 = unique(Stk1+1,Stk1+top1+1)-Stk1-1; 89         Dfs2(0,n/2+1);    sort(Stk2+1,Stk2+top2+1);    top2 = unique(Stk2+1,Stk2+top2+1)-Stk2-1; 90         Ans = Square = 0; 91          92         for(int i=1;i<=top1;i++) 93             Deal(i); 94          95          96         printf("%lld\n",Ans/2); 97 } 98  99 void Dfs(s64 A,s64 B,int T)100 {101         if(T>n)102         {103             Check(A,B);104             return;105         }106         Dfs(A+a[T],B,T+1);107         Dfs(A,B+a[T],T+1);108 }109 110 void Solve1()111 {112         Ans = Square = 0;113         for(int i=1;i<=n;i++) a[i]=get(),a[i]*=2;114         Dfs(0,0,1);115         printf("%lld\n",Ans/2);116 }117 118 int main()119 {120         srand(time(0));121         while(scanf("%d",&n)!=EOF)122         {123             if(n <= 25) Solve1();124             else Solve2(); 125         }126 }
View Code

 

【Foreign】咏叹 [模拟退火]