2020 杭电多校 第七场 1007(6850) Game

题意:

在一个二维平面内,给出 n n n个点。两个玩家在二维平面上玩游戏。

第一个玩家从第一个点开始操作,两个人轮流操作。游戏规则是,当前玩家从当前所在点跳向另外一个点,要求这个点没有被跳过,且本次跳跃的距离要比上一次跳跃的距离要长。特别的,第一次跳跃可以跳任意长度。

题解:

每次都从当前剩下的点集中,删去长度最长的边的端点,把这些点放到一个集合中。这个集合中可以跳跃的最长距离就是刚刚删去的距离。这样可以将给出的 n n n个点分成若干个集合,每一个集合中能跳的最长距离就是将它删去时候的距离。

官方题解给出的伪代码:

S = 点集
vector<set> v;
while(S非空){
    d = S集合中的点之间的最长距离
    S2 = 本轮要删除的点的集合,初始为空集
    for(auto p : S){
        if(S中存在一个点q,使得dis(p,q)==d,就说明p是当前集合最长边的端点,应当删去)
            S2.insert(p);
    }
    将S2中的点从S集合中删去
    v.push_back(S2);
}

我们有了这些集合该如何做决策呢。

首先,只有最后一个集合可能会出现这个集合只有一个的情况。因为如果这个集合中只有一个点,那么当前的最长距离就是0,也就说明它是最后一个点了,只有点多于一个最长距离就不是0。如果最长距离不是0,删去的点最少是两个。

我们最后判断结果就是根据最后一个集合进行判断的。

如果最后一个集合的个数是1,并且这个元素就是1号点,那么就是先手败;其它情况全是先手获胜。

获胜策略:

如果1号点不在最后一个集合,或者最后一个集合元素个数大于1。那么先手下一个选择跳跃的点就可以选在当前点所在集合中,跳最长距离。因为当前集合最长距离已经被跳了,那么后手下一个回合只能跳向别的集合来寻找比先手所跳距离更远的距离。后手会跳到一个新的前面的集合,往后面的集合跳是不能的,因为与后面集合的间距不可能大于当前集合内的最长距离。或者说后手根本找不到这样的新集合。后手跳到一个新的集合中,先手总能接着在本集合中跳。那么后手要不就是找不到新集合,要不就是前面压根都没有集合了,先手总是有的跳。所以后手就必败。

那如果1号点所在的集合只有一个元素。相当于刚刚的情况先手和后手身份交换了,先手要不断跳新集合了,那先手就必败。

代码:

其实我们发现,不需要将所有的点集都区分出来,我们只关心1号点所在集合的点的个数。

只要1号点在删除非零边的过程中被删去,那它所在的集合至少有两个点,也就是先手必胜的,否则就是后手必胜

求出所有的非零边,按边长排序。依次遍历每一条边,如果两个点都没有被删去,那么说明这个边能被删去,或者是一个新的最长边。只要1号点被删去,也就说明,1号点处于一个元素个数大于等于2的集合。否则就说明1号点被剩下了,成了最长距离是0的集合。

/*
 * @file 1007.cpp
 * @path D:\code\ACM\HDU\no.7\1007.cpp
 * @author Xiuchen
 * @date  2020-08-11 15:19:32
*/

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define DEBUG
#define dbg(x) cout << #x << " = "<< (x) << endl
#define dbg2(x1,x2) cout << #x1 << " = " << x1 << " " << #x2 << " = " << x2 << endl
#define dbg3(x1,x2,x3) cout<< #x1 << " = " << x1 << " " << #x2 << " = " << x2 << " " << #x3 << " = " << x3 <
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 2100;
int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}
int t;
int n;
struct node
{
    ll x, y;
} p[maxn];
struct dis
{
    int u, v;
    ll val;
};
ll fun(node a, node b){
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); 
}
bool cmp(dis a, dis b){
    return a.val > b.val;
}
vector<dis> v;
int vis[maxn];
int main(){
#ifdef DEBUG
    freopen("input.txt", "r", stdin);
//	freopen("output.txt", "w", stdout);
#endif
    scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        for(int i = 1; i <= n; i++){
            scanf("%lld%lld", &p[i].x, &p[i].y);
            vis[i] = 0;
        }
        ll mx = 0;
        v.clear();
        for(int i = 1; i <= n; i++){
            for(int j = i + 1; j <= n; j++){
                v.push_back(dis{i, j, fun(p[i], p[j])});
            }
        }
        sort(v.begin(), v.end(), cmp);
        bool flag = false;
        for(int i = 0; i < v.size(); i++){
            dis tmp = v[i];
            if(vis[tmp.u] == 1 || vis[tmp.v] == 1) continue;
            else{
                vis[tmp.u] = 1; 
                vis[tmp.v] = 1; 
                if(tmp.u == 1 || tmp.v == 1){
                    flag = true;
                    break;
                }
            }
        }
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

你可能感兴趣的:(思维,博弈论,acm竞赛)