1. 程式人生 > >TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization & Codeforces 839 E

TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization & Codeforces 839 E

name logs public cto 證明 數學 coder 最大化 配方

傳送門:https://284914869.github.io/AEoj/560.html

題目簡述:

定義"項"為兩個不同變量相乘。
求一個由多個不同"項"相加,含有n個不同變量的式子的最大值。
另外限制了每一個變量的最大最小值R[i]和L[i]和所有變量之和的最大值Max。

n<=13

題外話:

剛開始做這道題的時候,感覺意外眼熟?

codeforces 839 E(此題的退化版):http://codeforces.com/contest/839/problem/E

所以這裏將介紹兩道題的做法(證明)。

先來看

codeforces 839E

題意:給出一個圖的鄰接矩陣,要求給每個點賦一個>=0的值,使得點權和為K

,並定義每條邊權值為兩端點點權的乘積,要求最大化邊的權值和。

結論:最大化邊權就是要將k均分給圖中的最大團中的點。

證明:codeforces上給出了一種數學歸納法的證明:http://codeforces.com/blog/entry/53815

但這裏將介紹一種新的證明方法:

首先,現在有一種分配點權的方案,

a.對於兩個點a,b,假設之間沒有邊,且與a點相連的點權和為sa,與b點相連的點權和為sb。

再假設當前a的點權為pa,b的點權為pb。

因為全部的點權和=k,所以要維持pa+pb = 一個定值。

這兩個點對答案的貢獻是pa*sa+pb*sb

若sa>=sb,那麽(pa+pb)*sa+0*sb >= pa*sa+pb*sb,對答案的貢獻更大。所以把b的點權降為0更優。

若sa<=sb,那麽0*sa+(pa+pb)*sb >= pa*sa+pb*sb,對答案的貢獻更大。所以把a的點權降為0更優。

由此可見,存在一種最優的分配方案,任意不相連的兩個點,其中至少有一個點點權為0。

b.由a得到的結論可得,最優分配方案中,所有點權>0的點之間,兩兩都有邊(即團)。

我們來證明這個團中,每個點的點權相同

我們先給這個團中的每個點隨機賦一個權值(滿足權值和=k)。

若在這個團中並不是每個點的點權相同:

假設在這個團中,a,b權值pa不等於pb。(a與b相連)

設與a點相連的點權和(包括pb)為sa = k-pa,與b點相連的點權和(包括pa)為sb = k-pb

假設把a的點權變為pa+t,b的點權變為pb-t對邊的權值和的貢獻最大。

這時,邊的權值和的變化量為 t*(sa-pb) - t*(sb-pa) + (pa+t)*(pb-t) - pa*pb = - t*t + t*(sa-sb)

那麽這變成了一個二次函數最值問題(初中知識吧。。)

t=(sa-sb)/2=(pb-pa)/2的時候最優。

此時a權從pa-->(pa+pb)/2,b權從pb-->(pa+pb)/2。即pa,pb變為了它們的平均數。

所以,可以對這個團進行若幹個這樣的操作,

每次取兩個權值不相同的點,把它們的權值設為它們的平均數。

最終的最優方案,一定是每個點點權相同。

c.接下來我們證明,最大團最優。

若團的大小為s。邊的權值和為技術分享

s越大越好。

TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization

終於回到正題了。。。

若兩個變量的乘積對答案有貢獻,就將這兩個點之間連一條邊。與上題類似,唯一的區別就是:

L[i],R[i],Max的限制。並且,數據範圍變小。

其實證明方法類似。

a.存在一種最優的分配方案,任意不相連的兩個點,其中至少有一個點點權為為L[i]或R[i]。

證明方法同上。

b.就不一樣了

由a得到的結論可得,最優分配方案中,所有點權大於L[i],小於R[i]的點之間,兩兩都有邊(即團)。

這裏,設團中的點權和為tot。

設團中某兩個點a,b(設點權分別為pa,pb,設pa+pb=S)

設a,b連向團外的點權和分別為Wa,Wb

這兩個點對答案的貢獻是pa*pb + pa*(tot-S+Wa) + pb*(tot-S+Wb) = -pa^2 + pa*(S+Wa-Wb) + S*(tot-S+Wb)

又是一個二次函數最值問題。

pa = (S+Wa-Wb)/2,pb = (S+Wb-Wa)/2時

(除非pa或pb不在L到R範圍內,此時pa或pb有一項為L或R時更優,則a或b不會在團內,所以不考慮這種情況)

最優。

考慮pa和pb的特點:pa-Wa = (S-Wa-Wb)/2, pb-Wb = (S-Wa-Wb)/2。

於是pa - Wa = pb - Wb

所以這個團中的每一個點,pi - Wi是一個定值。

c.如何求解

一個很簡單的思路出來了。

枚舉哪些點權為L[i],哪些點權為R[i],其余點形成團。

這個枚舉的過程是3^n

這個基礎上求解,

對於團中的每一個點,可以輕易地求出W[i](定義見b)

也可以知道團的點權和 <= 一個值。設團的點權和 <= sum

由b的結論可得:團中p[i] - W[i]是一個定值。

設p[i] - W[i] = C

又p[i] = C+W[i] <= R[i],所以C <= R[i] - W[i]。

p[i] = C+W[i] >= L[i],所以C >= L[i] - W[i]。

同時sigma{p[i]}<=sum,所以sigma{ C+W[i] }<=R[i]。

由於點權總體越大越好,所以C越大越好。解上述不等式,求出最大的C。

最後求出在這種情況下圖的邊權和,更新答案,便做完了!

真是道好題!

註:可能我的方法不太優秀,歡迎各位大佬在評論區給出更方便的做法

這裏給出代碼:

 1 #include <cstdio>
 2 #include <string>
 3 #include <vector>
 4 #include <cstring>
 5 #include <iostream>
 6 #include <algorithm>
 7 using namespace std;
 8 #define _CLASSNAME_ BoundedOptimization
 9 #define _METHODNAME_ maxValue
10 #define _RC_ double
11 #define _METHODPARMS_ vector <string> s, vector <int> L, vector <int> R, int maxSum
12 #define ref(i,x,y)for(int i=x;i<=y;++i)
13 #define def(i,x,y)for(int i=x;i>=y;--i)
14 double tot, Ans;
15 int n, maxsum, w[13], W[13];
16 struct xint { int L, R; }p[13];
17 bool a[13][13];
18 bool isletter(char c) { return c >= a&&c <= z; }
19 double _min(double a, double b) { return a < b ? a : b; }
20 void work(int x) {
21     if (maxsum < 0)return;
22     if (x == n) {
23         double tmp = 2e9; int num = 0, sum = 0;
24         ref(i, 0, n - 1)if (w[i] < 0)tmp = _min(tmp, p[i].R - W[i]), ++num, sum += W[i];
25         tmp = _min(tmp, 1.0*(maxsum - sum) / num);
26         ref(i, 0, n - 1)if (w[i] < 0)if (tmp + W[i] < p[i].L)return;
27         double ans = tot, ans2 = (sum + num*tmp)*(sum + num*tmp);
28         ref(i, 0, n - 1)if (w[i] < 0)ans += (tmp + W[i])*W[i];
29         ref(i, 0, n - 1)if (w[i] < 0)ans2 -= (tmp + W[i])*(tmp + W[i]);
30         ans = ans + ans2 / 2;
31         if (ans > Ans)Ans = ans;
32         return;
33     }
34     int tmp = tot;
35     //first case
36     w[x] = p[x].L;
37     ref(i, 0, x - 1)if (w[i] < 0 && a[x][i])W[i] += w[x];
38     ref(i, 0, x - 1)if (w[i] >= 0 && a[x][i])tot += w[i] * w[x];
39     maxsum -= w[x]; work(x + 1); maxsum += w[x];
40     ref(i, 0, x - 1)if (w[i] < 0 && a[x][i])W[i] -= w[x];
41     tot = tmp;
42     //second case
43     w[x] = p[x].R;
44     ref(i, 0, x - 1)if (w[i] < 0 && a[x][i])W[i] += w[x];
45     ref(i, 0, x - 1)if (w[i] >= 0 && a[x][i])tot += w[i] * w[x];
46     maxsum -= w[x]; work(x + 1); maxsum += w[x];
47     ref(i, 0, x - 1)if (w[i] < 0 && a[x][i])W[i] -= w[x];
48     tot = tmp;
49     //third case
50     w[x] = -1; W[x] = 0;
51     ref(i, 0, x - 1)if (w[i] < 0 && !a[x][i])return;
52     ref(i, 0, x - 1)if (w[i] >= 0 && a[x][i])W[x] += w[i];
53     work(x + 1);
54     W[x] = 0;
55 }
56 class _CLASSNAME_ {
57 public:
58     _RC_ _METHODNAME_(_METHODPARMS_)
59     {
60         string S = "";
61         memset(a, 0, sizeof a);
62         memset(W, 0, sizeof W);
63         memset(w, 0, sizeof w);
64         Ans = 0; tot = 0;
65         ref(i, 0, s.size() - 1)S = S + s[i];
66         ref(i, 0, S.size() - 2)if (isletter(S[i]) && isletter(S[i + 1]))
67             a[S[i] - a][S[i + 1] - a] = a[S[i + 1] - a][S[i] - a] = 1;
68         n = L.size();
69         ref(i, 0, n - 1)p[i].L = L[i], p[i].R = R[i];
70         maxsum = maxSum;
71         work(0);
72         return _RC_(Ans);
73     }
74     // BEGIN CUT HERE
75 public:
76     void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); }
77 private:
78     template <typename T> string print_array(const vector<T> &V) { ostringstream os; os << "{ "; for (typename vector<T>::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << \" << *iter << "\","; os << " }"; return os.str(); }
79     void verify_case(int Case, const double &Expected, const double &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << \" << endl; cerr << "\tReceived: \"" << Received << \" << endl; } }
80     void test_case_0() { string Arr0[] = { "ba+cb" }; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = { 0,0,1 }; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = { 1,2,1 }; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 3; double Arg4 = 2.25; verify_case(0, Arg4, maxValue(Arg0, Arg1, Arg2, Arg3)); }
81     void test_case_1() { string Arr0[] = { "ab" }; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = { 0, 0, 10 }; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = { 20, 20, 20 }; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 12; double Arg4 = 1.0; verify_case(1, Arg4, maxValue(Arg0, Arg1, Arg2, Arg3)); }
82     void test_case_2() { string Arr0[] = { "ca+fc+fa+d","b+da+","dc+c","b","+ed+eb+ea" }; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = { 10,11,12,13,14,15 }; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = { 15,16,17,18,19,20 }; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 85; double Arg4 = 2029.25; verify_case(2, Arg4, maxValue(Arg0, Arg1, Arg2, Arg3)); }
83     void test_case_3() {
84         string Arr0[] = { "db+ea+ik+kh+je+","fj+lk+i","d+jb+h","a+gk+mb+ml+lc+mh+cf+fd+","gc+ka+gf+bh+mj+eg+bf+hf+l","b+al+ja+da+i",
85             "f+g","h+ia+le+ce+gi+d","h+mc+fe+dm+im+kb+bc+","ib+ma+eb+mf+jk+kc+mg+mk+","gb+dl+ek+hj+dg+hi","+ch+ga+ca+fl+ij+fa+jl+dc+dj+fk","+li+jg" }; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = { 57,29,50,21,49,29,88,33,84,76,95,55,11 }; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = { 58,80,68,73,52,84,100,79,93,98,95,69,97 }; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 845; double Arg4 = 294978.3333333333; verify_case(3, Arg4, maxValue(Arg0, Arg1, Arg2, Arg3));
86     }
87 
88     // END CUT HERE
89 };
90 // BEGIN CUT HERE
91 
92 int main() {
93     _CLASSNAME_ user;
94     user.run_test(-1);
95     getchar();
96 }
97 // END CUT HERE

TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization & Codeforces 839 E