Luogu PP1946 Olympic___线性规划

题目大意:

奥运会正在如火如荼的进行着,金牌榜上也有许多队伍需要排名。你需要选择三个整数Pg,Ps和Pb,分别表示每获得一块金、银、铜牌所对应得分。并且满足1000>=Pg>=Ps>=Pb>=1.队伍将依据他们获得的分数进行排序(高分在前)。现在,为了使你所在的队伍排名尽可能的靠前,由你来选择Pg,Ps,Pb。

1<=n<=15
0<=G,S,B<=100000

分析:

显然暴力枚举各个分数,然后T炸天
考虑优化:
枚举Pg,
枚举Ps,
到枚举Pb时,
设第i个队伍Pg,Ps的得分是 s u m i sum_i sumi
那么第一个队伍要赢过它,
假设Pb是num,就有:
s u m 1 + P b 1 ∗ n u m > = s u m i + P b i ∗ n u m sum_1+Pb_1*num>=sum_i+Pb_i*num sum1+Pb1num>=sumi+Pbinum
( P b 1 − P b i ) ∗ n u m > = s u m i − s u m 1 (Pb_1-Pb_i)*num>=sum_i-sum_1 (Pb1Pbi)num>=sumisum1
然后我们讨论 ( P b 1 − P b i ) (Pb_1-Pb_i) (Pb1Pbi)
第一种:
> 0 >0 >0
那么就有 n u m > = ( s u m i − s u m 1 ) / ( P b 1 − P b i ) num>=(sum_i-sum_1)/(Pb_1-Pb_i) num>=(sumisum1)/(Pb1Pbi)
这时候如果 s u m i − s u m 1 < = 0 sum_i-sum_1<=0 sumisum1<=0则没贡献,否则存在 n u m > 0 num>0 num>0使1胜i,将不等式计入
第二种:
< 0 <0 <0
那么就有 n u m < = ( s u m i − s u m 1 ) / ( P b 1 − P b i ) num<=(sum_i-sum_1)/(Pb_1-Pb_i) num<=(sumisum1)/(Pb1Pbi)
这时候如果 s u m i − s u m 1 > = 0 sum_i-sum_1>=0 sumisum1>=0则没贡献,否则则存在 n u m > 0 num>0 num>0使1胜i,将不等式计入
第三种:
= 0 =0 =0,
那么这时候只需要满足 s u m 1 > = s u m i sum_1>=sum_i sum1>=sumi即可

对于所有要求 n u m < = 某 个 数 num<=某个数 num<=的不等式,我们将所有的个数加起来表示为选择分数为1时最多能战胜的队伍,
然后我们将分数递增的去枚举,当越过一个>=时答案+1,越过一个<=答案-1即可

代码:

#include 
#include 
#include 
#include 
#include 
#include 

#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define mt(x) memset(x, 0, sizeof(x))
#define mp(x, y) memcpy(x, y, sizeof(y))
#define lson(x) x * 2 
#define rson(x) x * 2 + 1

#define M 1000
#define N 20

using namespace std;
 
typedef long long ll;

struct Code {
	int A, B;
	Code(){};
	Code(int a, int b) {
		A = a, B = b;
    }
}tmp[N];
struct Node {
	int G, S, B;
}C[N];
int f[N][2], zyf[M+5], sum[N], n, max1, max2, max3, maxn = -1;

int read(int &x) {
	int f = 1; x = 0; char s = getchar();
	while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
	while (s >= '0' && s <= '9') { x = x * 10 + (s - '0'); s = getchar(); }
	return x * f;
}

void write(int x) {
	if (x < 0) printf("-"), x = - x;
    if (x > 9) write(x / 10); 
	putchar(x % 10 + '0');
}

bool cmp(Code a, Code b)
{
    return a.A < b.A;
}

int main() {
	read(n);
	rep(i, 1, n) read(C[i].G), read(C[i].S), read(C[i].B);
	rep(k, 1, 1000) 
        rep(l, 1, k) {
            int num = 0, cnt = 0;
            rep(i, 1, n) sum[i] = C[i].G * k + C[i].S * l;
            rep(i, 2, n) {
                double num1 = sum[i] - sum[1], num2 = C[1].B - C[i].B;
                if (!num2) { num += (sum[1] >= sum[i]); continue; }
                if (num2 < 0 && num1 >= 0) continue;
                if (num2 > 0 && num1 <= 0) { num++; continue; }
                if (num2 > 0) tmp[++cnt] = Code(ceil(num1 / num2), 1);
                         else tmp[++cnt] = Code(floor(num1 / num2), 0);
            }
            sort(tmp + 1, tmp + cnt + 1, cmp);
            int L = 1, now = 0;
            rep(i, 1, cnt) if (!tmp[i].B) num++;
            while (L <= cnt) {
                int i = L, willcut = 0;
                if (tmp[i].B) num++; else willcut--;
                while (tmp[i + 1].A == tmp[i].A && i + 1 <= cnt) {
		            if (tmp[i + 1].B) num++; else willcut--; 
		            i++;
		        }
         	    if (num > maxn && tmp[i].A >= 1 && tmp[i].A <= l)
                    maxn = num, max1 = k, max2 = l, max3 = tmp[i].A;
                L = i + 1, num += willcut;
            }   
	    }
	if (maxn == -1) printf("1 1 1\n"); else printf("%d %d %d\n", max1, max2, max3);
	return 0;
}

你可能感兴趣的:(暴力/枚举/模拟,C++,规律与思维)