1. 程式人生 > 其它 >11.4 校內模擬賽解題報告

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;
}