【Foreign】咏叹 [模拟退火]
咏叹
Time Limit: 100 Sec Memory Limit: 256 MBDescription
有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组。
Solution
首先,必然是全部木棍都用上的时候最优,对于n=2的时候,显然就是分三种情况讨论一下就好了。
然后我们从n=2的情况拓展。发现,其实可以把多个木棍并在一起,使其变为n=2的情况,然后讨论。那么现在答案只和两段的长度有关了。
但是直接暴力搜索是O(2^40)的,显然不行,我们考虑分为两部分来搜索,搜索前n/2个,和后n/2个,表示选不选得到的价值,现在效率是O(2*2^20)。
然后怎么得到答案呢?显然:如果我们设宽为x,则长为tot-2x(tot为总长),那么这是一个二次函数,必然有峰值。
所以我们大胆猜测,我们确定了一半,另外一半使得其答案最优的话也可能满足有峰值的性质。
然后我们固定一半,另一半运用模拟退火求解即可!
Code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }