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

传送门: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
时间: 2024-10-07 09:30:06

TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization & Codeforces 839 E的相关文章

TopCoder SRM 558 Div 1 - Problem 1000 SurroundingGame

题目简述  一个人在一个n * m棋盘上玩游戏,想要占领一个格子有两个方法: 在这个格子放一个棋子.  这个格子周围(四联通)的格子中**都有棋子**. 在(i, j)中放棋子需要花费cost[i][j],占领(i, j)能获得benefit[i][j].求一种放置棋子的方法,使得总收益(收益 - 花费)最大. 2<=n,m<=20 分析 一眼看上去, 状压? 我是不是dp学傻了..根本想不出 网络流? 嗯,此题是一道非常套路的网络流练习题. 如果想不到对棋盘进行黑白染色,就GG了. 所以套路

TopCoder SRM 561 Div 1 - Problem 1000 Orienteering

传送门:https://284914869.github.io/AEoj/561.html 题目简述: 题外话: 刚开始看题没看到|C|<=300.以为|C|^2能做,码了好久,但始终解决不了一棵树中多条直径去重的问题.后来才发现|C|<=300,暴力就可以了. 不知道有哪位大佬会|C|^2的做法?? 思路: 很显然,若length为树中经过所有S中的点的最短路径长度, 若size为虚树中所有边的长度和, 若dis为虚树中的最远点对的距离(即直径长度) 那么length=size*2-dis.

TopCoder SRM 559 Div 1 - Problem 900 CircusTents

题目简述: n个实心圆,两两没有交集,在第一个圆上找一个点,使得它到另外一个圆上某个点的最短距离的最小值尽量大,两个点之间的最短距离是指连接两个点且中途不进入任何一个实心圆内部的路径的长度的最小值. 二分答案: 很显然,这题跟二分答案有关. 思路: 我们先考虑,如果第一个圆上的点确定了下来,它到别的所有的圆的距离的最小值. The First Case: The Second Case: 图中蓝色线是最短的路径. 当然还有第三种情况,就是路径过程中与别的圆相交了.不过这样肯定不是所有的路中最短的

TopCoder SRM 628 DIV 2

250-point problem Problem Statement    Janusz is learning how to play chess. He is using the standard chessboard with 8 rows and 8 columns. Both the rows and the columns are numbered 0 through 7. Thus, we can describe each cell using its two coordina

TopCoder SRM 634 Div.2[ABC]

TopCoder SRM 634 Div.2[ABC] ACM 题目地址: TopCoder SRM 634 赛后做的,感觉现场肯定做不出来Orz,简直不能多说. Level One-MountainRanges[水题] 题意: 问序列中有几个完全大于旁边的峰. 分析: 傻逼题,不多说. 代码: /* * Author: illuz <iilluzen[at]gmail.com> * File: one.cpp * Create Date: 2014-09-26 21:01:23 * Desc

TopCoder SRM 596 DIV 1 250

body { font-family: Monospaced; font-size: 12pt } pre { font-family: Monospaced; font-size: 12pt } Problem Statement      You have an array with N elements. Initially, each element is 0. You can perform the following operations: Increment operation:

[topcoder]SRM 633 DIV 2

第一题,http://community.topcoder.com/stat?c=problem_statement&pm=13462&rd=16076 模拟就可以了. #include <vector> #include <algorithm> using namespace std; class Target { public: vector <string> draw(int n) { vector<string> result(n,

[topcoder]SRM 646 DIV 2

第一题:K等于1或者2,非常简单.略.K更多的情况,http://www.cnblogs.com/lautsie/p/4242975.html,值得思考. 第二题:http://www.cnblogs.com/lautsie/p/4245242.html BFS和DFS都可以,注意的是,写的时候,可以往que里几个东西一起扔,就不用建立对象了.也可以直接用二维矩阵记录blocked和visited. 剪枝什么的,最基本的是发现其实在步数限制的情况下,棋盘就是有界的了. 第三题:http://ap

Topcoder SRM 638 DIV 2 (大力出奇迹)

水题,就是一个暴力.大力出奇迹. Problem Statement   There is a narrow passage. Inside the passage there are some wolves. You are given a vector <int> size that contains the sizes of those wolves, from left to right. The passage is so narrow that some pairs of wolv