球hack错误思路
查看原帖
球hack错误思路
632868
Clover_BY楼主2022/9/21 12:16

今天校内模拟赛考了这道题
考场上想了个贪心策略:分别维护两个数组,第一个 (sz1)(sz_1) 按照 aia_i 升序排序,第二个 (sz2)(sz_2) 按照 bib_i 升序排序。(后来发现这样做其实不全面,于是稍微改了一下,后面会说)

首先如果确定了在某些州一定要得到协助人,那么得到他们一定要在前往其他州之前;前往各个州得到协助人的顺序应该按照这些州 bib_i 的顺序,小的在前大的在后,用 sz2sz_2 维护。

确定思路是从小到大枚举得到协助人的数目,同时就确定了要先前往那些州,累加这一部分的贡献。之后优先前往 aia_i 较小的州,如果与之前处理的那一部分重合则直接修正,否则删去 sz1sz_1 要选的那一部分中最后一位,保证剩余代价最小。这里排序会出bug,因此改了一下。代码如下:

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;

inline int read()
{
    int x = 0; char c; bool f = false;
    while(!isdigit(c = getchar()))
        if(c == '-') f = true;
    do{
        x = (x << 1) + (x << 3) + (c ^ 48);
    }while(isdigit(c = getchar()));
    return f ? -x : x;
}

int n, k, rb, tl, tp = 1;
bool arv[5005], dealt[5005];
double mem = 1.0, ans = 0.0, tot = 0.0, sum = 0.0;
struct node
{
    int id;
    double a;
    double b;
}sz[5005], zs[5005];
//选票优先、协作优先
inline bool tic(node a, node b)
{
    if(a.a != b.a) return a.a < b.a;
    if(a.b != b.b) return a.b > b.b;
    return a.id < b.id;
}
inline bool par(node a, node b)
{
    if(a.b != b.b) return a.b < b.b;
    if(a.a != b.a) return a.a > b.a;
    return a.id > b.id;
}

int main()
{
    n = read(); k = tl = read();//最坏从最尾部开始
    for(register int i = 1; i <= n; ++i)
    {
        sz[i].a = read();
        sz[i].b = read();
        sz[i].id = i; zs[i] = sz[i];
    }
    sort(sz + 1, sz + n + 1, tic);
    sort(zs + 1, zs + n + 1, par);
    while(zs[tp].b == -1) ++tp;//之后从这里开始即可
    for(register int i = 1; i <= k; ++i)
        ans += (double)sz[i].a;
    rb = min(n, tp + k - 1); tot = ans;
    for(register int i = tp; i < rb; ++i)//最不济前面都拿到协作者然后一起攻占最后一个州
    {
        arv[zs[i].id] = true;
        sum = tot; mem += 1.0;
        bool rep = false;
        for(register int j = 1; j <= tl; ++j)
        {
            if(arv[sz[j].id] && !dealt[sz[j].id])
            {
                rep = dealt[sz[j].id] = true;
                sum -= (double)sz[j].a;//此时统计出来的就是去掉这些州以后的剩余
            }
        }
        if(!rep) sum -= (double)sz[tl].a, --tl; tot = sum;
        sum /= (double)mem; sum += (double)zs[tp].b;
        for(register int j = tp + 1; j <= i; ++j)
            sum += (double)zs[j].b / (double)(j - tp + 1.0);
        ans = min(ans, sum);
    }
    printf("%.14lf", ans); return 0;
}
//显然如果确定要在这些城市演讲,同时演讲与分开演讲造成的结果相同
//所以要考虑得到选票的容易度和得到协作者的容易度
//处理的时候将双方排个序,每次把协作者里最大的一个拿出来替换掉选票里最大的一个,观察是否有解
//决策造成的结果应该要求单调递减
//亦即不会存在在大的点拿到协作者而在小的点使用的情况
//得到协作者的前提上一定已经得到了选票
//不过如果要求得到这名协作者,先得到一定比后得到优

但是这玩意 WAWA 得挺惨
请问这种思路的漏洞在什么地方?

(如果能用下面这组样例说明更好)

7
5
393 646
375 666
374 676
320 635
288 668
284 758
333 702

(THX)

2022/9/21 12:16
加载中...