HDU-6850 Game
阿新 • • 發佈:2020-08-12
title: HDU-6850 Game
date: 2020-08-12 20:00:00
categories:
- 博弈論
tags: - 記憶化搜尋
HDU-6850 \(Game\)
題目連結 http://acm.hdu.edu.cn/showproblem.php?pid=6850
如果感覺不適請前往 [https://vagrantac.info/2020/08/12/杭電多校/HDU6850 Game/](https://vagrantac.info/2020/08/12/杭電多校/HDU6850 Game/)
題解
我使用的是標程的第二種解法,和當時在訓練的想法是一樣的,
首先簡述一下我的想法,
以下直接忽略一個點不能走多次
使用陣列 \(dp[x][y]\)
也可以將 \(x\) 到 \(y\) 看作是一條邊, \(0\) 表示走 \(x\) 到 \(y\) 這條邊為必敗態,否則為必勝態。
之後使用記憶化搜尋進行搜尋一下,
如果一條邊 \(x\) 到 \(y\) 中 \(y\) 下一步沒有點可走的話表示這個狀態為必敗態。
對於一個狀態,如果它的下一個可以到達的所有的狀態中存在必敗態,\(namo\) 這個狀態為必勝態,
否則為必敗態。(關於必勝態和必敗態,詳細可以看一下博弈論)
下面是證明一下
這個題我上面的解法會有一個問題,就是出現環怎麼辦。
環的話,分類考慮一下就好了
如果 \(dp[x][y]\) 存在下一步狀態並且存在 \(z\) 使得 \(dp[y][z] = 0\) ,那直接走這條邊就好了,不需要考慮其他邊,並且保證了必勝態了。
否則的話對於所有的 \(z\) 保證 \(dp[y][z] = 1\) ,那到達這個位置的時候註定了必敗態了。
這個問題證明很簡單,當然也是我第一次手推證明,之前的都是盲猜和結論題,推就完事了。
程式碼
#include <cmath> #include <cstdio> #include <iostream> using namespace std; const int M = 2e3+55; typedef long long ll; ll cost[M][M]; int dp[M][M]; ll x[M], y[M]; int n; int dfs(int xx, int yy) { if (dp[xx][yy] != -1) return dp[xx][yy]; int b = 0; for (int i = 0; i < n; ++ i) { if (i == yy) continue; if (cost[yy][i] > cost[xx][yy]) { if (dfs(yy, i) == 0) { dp[xx][yy] = 1; return 1; } else b ++; } } dp[xx][yy] = 0; return dp[xx][yy]; } int main() { int t; scanf("%d", &t); while (t --) { scanf("%d", &n); for (int i = 0; i < n; ++ i) { scanf("%lld%lld", &x[i], &y[i]); } for (int i = 0; i < n; ++ i) { for (int j = 0; j < n; ++ j) { cost[i][j] = (x[i]-x[j]) * (x[i]-x[j]) + (y[i]-y[j]) * (y[i]-y[j]); dp[i][j] = -1; } } int a = 0, b = 0; for (int i = 1; i < n; ++ i) { if (dfs(0, i) == 0) { a ++; break; } else b ++; } if (a) printf("YES\n"); else printf("NO\n"); } return 0; }