11.4 校内模拟赛解题报告

T1

尽量先找面额大的,然后找面额小的。

int main()
{
	n = read();
	for(int i = 1; i <= n; i++)
	{
		int x = read();
		if(x == 5) a++;
		if(x == 10)
		{
			if(a != 0) a--, b++;
			else {puts("NO"); return 0;}
		}
		if(x == 20)
		{
			if(b != 0 && a != 0) a--, b--;
			else if(a >= 3) a -= 3;
			else {puts("NO"); return 0;}
		}
	}
	puts("YES");
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2

考场上写了一个用栈正序维护的错误做法,维护栈中每个数字的个数,和已知出口数字的个数,怎么看怎么对。但是并不对。
正解:用栈倒叙维护,每次栈顶的绝对值与当前数的绝对值不相同,就将当前数变成负数进栈,如果相同且栈顶为负,则出栈。

/*
Date:
Source:
Knowledge: 用栈维护,倒着来 
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#define orz cout << "AK IOI" << "\n"

using namespace std;
const int maxn = 1e6 + 10;

int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int Max(int a, int b){
	return a > b ? a : b;
}
int Min(int a, int b){
	return a < b ? a : b;
}
int n, m, a[maxn], vis[maxn];
int top, st[maxn];
int main()
{
	//freopen("program1.in", "r", stdin);
	//freopen("program.out", "w", stdout);
	n = read();
	for(int i = 1; i <= n; i++) a[i] = read(), vis[a[i]]++;
	for(int i = 1; i <= n; i++)
		if(vis[a[i]] % 2 == 1) {puts("NO"); return 0;}
	m = read();
	for(int i = 1; i <= m; i++)
	{
		int x = read();
		if(a[x] > 0) a[x] = -a[x];    	
	}
	for(int i = n; i >= 1; i--)
	{
		if(abs(st[top]) != abs(a[i])) 
		{
			if(a[i] > 0) st[++top] = -a[i], a[i] = -a[i];
			else st[++top] = a[i];
		}
		else
		{
			if(a[i] > 0) top--;
			else st[++top] = a[i];
		}
	} 
	for(int i = 1; i <= n; i++) 
	{
		if(a[i] < 0) printf("%d ", a[i]);
		else printf("+%d ", a[i]);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3

将钥匙看做左括号,门看做右括号,模拟括号匹配,将问题转化为 从 \(a\) 走到 \(b\) 能否完成括号匹配。

宽搜 + DP。

\(f[i][j][k]\) 表示点 \(i\) 到点 \(j\) 是否有状态 \(k\)
\(k = 0\) 表示 \(i\)\(j\) 的路径上能够括号匹配。
\(k:1 — 10\) 表示点 \(i\)\(j\) 的路径上,栈顶为右括号 \(k\)
\(k:11 — 20\) 表示点 \(i\)\(j\) 的路径上,栈顶为左括号 \(k\),即缺右括号 \(k\)

每次更新一个 \(i \ j \ k\),就相当于在 \(i \ j\) 之间连一条状态为 \(k\) 的边,让这条边进队。
\(w < 0\) 的时候边是不进队的,因为有了左括号后可以接右括号,而右括号后不能接左括号。

从队列里取出从 \(u\)\(v\) 的状态为 \(w\) 的边。
如果 \(w = 0\),那么枚举状态 \(k (0, 11—20)\) 进行更新。
如果 \(w != 0\),那么只能去找右括号。
具体看代码。
为什么 \(w=0\) 是双向更新,\(w!=0\) 是单项更新?
因为只能是栈中有左括号的情况下,只有右括号才能入栈。
对于每一次询问,判断 \(f[u][v][0]\) 即可。

/*
Date:
Source:
Knowledge:f[i][j][k] 表示点i到点j 是否有状态k
*/
#include <iostream>
#include <cstdio>
#include <queue>
#define orz cout << "AK IOI" << "\n"

using namespace std;
const int maxn = 110;

int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch <= '9' && ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void print(int X)
{
	if(X < 0) X = ~(X - 1), putchar('-');
	if(X > 9) print(X / 10);
	putchar(X % 10 ^ '0');
}
int Max(int a, int b){
	return a > b ? a : b;
}
int Min(int a, int b){
	return a < b ? a : b;
}
int n, m, Q;
bool f[maxn][maxn][25];
struct node{
	int u, v, w;
};
queue <node> q;
int main()
{
	n = read(), m = read();
	for(int i = 1; i <= n; i++) f[i][i][0] = 1; 
	for(int i = 1; i <= m; i++)
	{
		int u = read(), v = read(), w = read();
		if(w == 0)
		{
			f[u][v][w] = f[v][u][w] = 1;
            q.push((node){u, v, w}); 
            q.push((node){v, u, w}); 
		}
		else if(w > 0)//钥匙  
		{
			w += 10;
            f[u][v][w] = f[v][u][w] = 1;
            q.push((node){u, v, w}); 
            q.push((node){v, u, w}); 
		}
		else w = -w, f[u][v][w] = f[v][u][w] = 1; //门 
	}
	while(!q.empty())
	{
		node t = q.front();
		q.pop();
		int u = t.u, v = t.v, w = t.w;
		if(w == 0)//大前提是已经有一条权值为 0 的边连通 u,v, 此时是一条有向边, 类比最短路。 
		{
			for(int i = 1; i <= n; i++)
			{
				if(f[i][u][0] && !f[i][v][0]) 
            	{
            	    f[i][v][0] = 1;
           	     	q.push((node){i, v, 0});
            	}
            	if(f[v][i][0] && !f[u][i][0])
            	{
            	    f[u][i][0] = 1;
            	    q.push((node){u, i, 0});
            	}
				for(int k = 11; k <= 20; k++)
				{
					if(f[i][u][k] == 1 && !f[i][v][k]) f[i][v][k] = 1, q.push((node){i, v, k});
					//if(f[i][v][k] == 1 && !f[i][u][k]) f[i][u][k] = 1, q.push((node){i, u, k});
				}
			}
		}
		else 
		{
			for(int i = 1; i <= n; i++)
            {
                if(f[v][i][w - 10] && !f[u][i][0]) // w 属于(1 - 10) 并没有入队 
                {
                    f[u][i][0] = 1;
                    q.push((node){u, i, 0});
                }
            }
		}
	}
	Q = read();
	for(int i = 1; i <= Q; i++)
	{
		int a = read(), b = read();
		if(f[a][b][0] == 1) puts("YES");
		else puts("NO");
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

posted @ 2021-11-04 17:35  _程门立雪  阅读(44)  评论(3编辑  收藏  举报