比赛-sparrow学长的训练赛2 (22 Aug, 2018)

A. 井字棋##

记忆化搜索水过。当 \(9\) 个格子,每个有未讨论、黑、白三个状态时,一个较大的搜索总次数是两万左右。但实际由于双方轮流出牌,并且一旦获胜就终止游戏,实际上的状态数只有 \(5000\) 左右。 \(f_S\) 表示当前局面的白方胜利、失败、或者平局。考虑后面能到的状态 \(S'\) 如果有一个是胜利,则当前状态胜利。否则如果有一个是平局,则当前状态平局。除此之外,就是失败了。

#include <cstdio>
#include <cstring>

using namespace std;

int f[50000], A[15], G[15];
char str[15];

inline int hs(int *arr)
{
	int s = 0;
	for (int i = 0; i < 9; ++i)
		s = s * 3 + arr[i];
	return s;
}

int ck(int *arr, int a, int b, int c, int &d)
{
	if (arr[a]==0&&arr[b]==0&&arr[c]==0) return d = 2;
	if (arr[a]==1&&arr[b]==1&&arr[c]==1) return d = 1;
	return d = 0;
}

void dfs(int *A, int s, bool p)
{
	int t;
	if (f[s] != -1) return;
	if (ck(A,0,1,2,t)||ck(A,3,4,5,t)||ck(A,6,7,8,t)||ck(A,0,3,6,t)||ck(A,1,4,7,t)||ck(A,2,5,8,t)||ck(A,0,4,8,t)||ck(A,2,4,6,t)) {
		if (t == 1)
			f[s] = 1;
		else
			f[s] = 0;
		return;
	}
	bool ee = 0, possi = 0;
	for (int i = 0; i < 9; ++i) {
		if (A[i] == 2) {
			A[i] = p;
			int tmp = hs(A);
			dfs(A, tmp, p ^ 1);
			if (f[tmp] == p)
				f[s] = p;
			if (f[tmp] == 2)
				ee = 1;
			possi = 1;
			A[i] = 2;
		}
	}
	if (!possi)
		f[s] = 2;
	else if (f[s] == -1)
		f[s] = ee ? 2 : p ^ 1;
	return;
}

int main()
{
//	freopen("well.in", "r", stdin);
//	freopen("well.out", "w", stdout);
	
	int T;
	for (int i = 0; i < 9; ++i)
		A[i] = 2;
	memset(f, -1, sizeof f);
	dfs(A, hs(A), 0);
	scanf("%d", &T);
	while (T--) {
		scanf("%s%s%s", str, str + 3, str + 6);
		for (int i = 0; i < 9; ++i) {
			if (str[i] == 'O')
				G[i] = 0;
			else if (str[i] == 'X')
				G[i] = 1;
			else
				G[i] = 2;
		}
		int tmphs = hs(G);
		if (f[tmphs] == 0)
			printf("O\n");
		else if (f[tmphs] == 1)
			printf("X\n");
		else if (f[tmphs] == 2)
			printf("E\n");
		else {
			for (int hh = 0; hh <= -4; ++hh);
		}
	}
	return 0;	
}

B. 午餐##

状态 \(f_{i,j}\) 表示第 \(i\) 个面包处于 \(j\) 状态时,\([1, i-1]\) 这些人的选择是否合法。状态有 \(4\) 个:被 \(i\) 吃、被 \(i-1\) 吃、同时被两人吃、不被吃。然后讨论一下可以转移到 \(f_{i+1, j'}\) 。由于要记录答案,所以 \(f_{i+1,j'} =j\) ,这样最后可以倒着更新答案。说起来略复杂,具体见代码吧 Orz 。

#include <stdio.h>
#include <ctype.h>
#include <string.h>
  
using namespace std;
  
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<typename T>
void rd(T &num)
{
    char tt;
    bool flag = 0;
    while (!isdigit((tt = GC)));
    num = tt - '0';
    while (isdigit(tt = GC))
        num = num * 10 + tt - '0';
    return;
}
  
template<typename T>
void pt(T num)
{
    if (num < 0) putchar('-'), num = -num;
    char sss[20];
    int top = 0;
    do sss[++top] = (num % 10 + '0');
    while (num /= 10);
    while (top)
        putchar(sss[top--]);
    putchar(' ');
    return;
}
  
const int _N = 1200000;
  
int A[_N], f[_N][4], ans[_N], N;
  
bool test(int t)
{
    memset(f, -1, sizeof f);
    f[1][t] = 0;
    for (int i = 1; i <= N; ++i) {
        if (~f[i][0]) {
            if (A[i] <= A[i + 1]) f[i + 1][1] = 0;
            if (A[i] * 2 <= A[i + 1]) f[i + 1][3] = 0;
        }
        if (~f[i][1]) {
            if (A[i] <= A[i + 1] * 2) f[i + 1][1] = 1;
            if (A[i] <= A[i + 1]) f[i + 1][3] = 1;
        }
        if (~f[i][2]) {
            if (A[i] >= A[i + 1]) f[i + 1][0] = 2;
            if (A[i] * 2 >= A[i + 1]) f[i + 1][2] = 2;
        }
        if (~f[i][3]) {
            if (A[i] >= A[i + 1] * 2) f[i + 1][0] = 3;
            if (A[i] >= A[i+1])f[i + 1][2] = 3;
        }
    }
    if (~f[N + 1][t]) {
        for (int i = N + 1; i >= 1; --i) {
            if (t == 1) ans[i - 1] = i;
            else if (t == 2) ans[i] = i;
            else if (t == 3) ans[i] = ans[i - 1] = i;
            t = f[i][t];
        }
        for (int i = 1; i <= N; ++i)
            pt(ans[i] > N ? ans[i] - N : ans[i]);
        return 1;
    }
    return 0;
}
  
int main()
{
    rd(N);
    for (int i = 1; i <= N; ++i)
        rd(A[i]);
    A[N + 1] = A[1];
    for (int i = 0; i < 4; ++i) {
        if (test(i)) break;
    }
    return 0;
}

C. 泰拉瑞亚##

单调栈 \(O(nm)\) 水过 40 分。正解是分块 + 线段树维护。

posted @ 2018-08-22 16:33  derchg  阅读(152)  评论(0编辑  收藏  举报