SRM 560

好神的思路- -  居然用图论转姿势化式子


因为是很多两个变量的积加起来,那么转化成一张图,就是右边的两个点的值乘积,求个和。


设想最后得到一个答案,如果某两个点不相邻,并且这两个点没有取到最值,点A周围点的权值和是suma,点B周围点的权值和是sumb。

那么如果suma>sumb,那么可以增加A的值减少B的值使得答案更大,然后A,B中肯定一个会达到最值。


所以,没有取到最值的点肯定是个团。就可以3^15枚举哪些取得最大值,哪些最小值,哪些没有取到最值。


对于没有取到最值的点,假设剩下的可分配价值是M,每个点周围sumi,每个点的价值是vi,k是团的个数

那么可以列出式子:对每个i求和(vi*(sumi+(M-vi)/2)) 就是求这个的最大值,M-vi是团中剩下点的权值和,除以2是因为会重复算两次。

然后要求在sum(vi)=M的条件下,求上式的极值,用拉格朗日乘数法带入求偏导,得到 vi = sumi + (M - 求和(sumi))/k


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>

using namespace std;

class BoundedOptimization {
public:
	double maxValue(vector<string> , vector<int> , vector<int> , int);
};
int gg[55][55];
double val[55];
double BoundedOptimization::maxValue(vector<string> expr,
		vector<int> lowerBound, vector<int> upperBound, int maxSum) {
	int i, j, k;
	int ch, nch, chmx, chmn;
	string ex;
	double ans, p;
	bool flag;
	ex.clear();
	for (i = 0; i < expr.size(); ++i)
		ex = ex + expr[i];
	memset(gg, 0, sizeof(gg));
	for (i = 0; i < ex.size(); i += 3) {
		gg[ex[i] - 'a'][ex[i + 1] - 'a'] = 1;
		gg[ex[i + 1] - 'a'][ex[i] - 'a'] = 1;
	}
	int n = lowerBound.size();
	ans = 0;
	for (nch = 0; nch < (1 << n); ++nch) {
		flag = true;
		for (i = 0; i < n; ++i) {
			if ((nch & (1 << i)) == 0)
				continue;
			for (j = i + 1; j < n; ++j) {
				if ((nch & (1 << j)) == 0)
					continue;
				if (gg[i][j] == 0)
					flag = false;
			}
		}
		if (!flag)
			continue;
		ch = ((1 << n) - 1) ^ nch;
		for (chmx = ch;; chmx = ((chmx - 1) & ch)) {
			flag = true;
			chmn = (ch ^ chmx);
			double M = maxSum;
			k = 0;
			for (i = 0; i < n; ++i) {
				if (chmx & (1 << i)) {
					val[i] = upperBound[i];
					M -= upperBound[i];
				} else if (chmn & (1 << i)) {
					val[i] = lowerBound[i];
					M -= lowerBound[i];
				} else
					k++;
			}
			if (M < 0) {
				if (chmx == 0)
					break;
				continue;
			}
			double suma = 0;
			for (i = 0; i < n; ++i) {
				if (nch & (1 << i)) {
					for (j = 0; j < n; ++j) {
						if (gg[i][j] == 0)
							continue;
						if (chmx & (1 << j))
							suma += upperBound[j];
						else if (chmn & (1 << j))
							suma += lowerBound[j];
					}
				}
			}
			double t = (M - suma) / k;
			for (i = 0; i < n; ++i) {
				if (nch & (1 << i)) {
					double a = 0;
					for (j = 0; j < n; ++j) {
						if (gg[i][j] == 0)
							continue;
						if (chmx & (1 << j))
							a += upperBound[j];
						else if (chmn & (1 << j))
							a += lowerBound[j];
					}
					val[i] = a + t;
					if (val[i] < lowerBound[i] || val[i] > upperBound[i])
						flag = false;
				}
			}
			if (!flag) {
				if (chmx == 0)
					break;
				continue;
			}
			p = 0;
			for (i = 0; i < n; ++i) {
				for (j = i + 1; j < n; ++j) {
					if (gg[i][j])
						p += val[i] * val[j];
				}
			}
			ans = max(ans, p);
			if (chmx == 0)
				break;
		}
	}
	return ans;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值