20200103搜索專場

寫在前面

三道題全是搜索,前兩道傻逼題,T3碼量巨長node

指望 \(AK\) ,實際得分 \(60 + 100 + 65 = 225pts\)ios

讀錯題的都是士兵git

T1

數學題(math.cpp/c/pas)算法

Solution

數據都很小,暴力搜索便可數組

注意枚舉第一個數可取的範圍,注意全部數都必須是正整數spa

Code

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
評測機別偷懶,賽後給您加雞腿 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1;
const int INF = 1;
const int mod = 1;

int n, S, p;
int cnt = 0;

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

void dfs(int pos, int val, int pre){
//	printf("%d\n", val);
	if(pos == n + 1) {
		if(val == S) cnt++;
		return ;
	}
	int cnt = n - pos + 1;
	if(cnt * pre + (cnt + 1) * cnt / 2 * p + val < S) return ;
	if(cnt * pre - (cnt + 1) * cnt / 2 * p + val > S) return ;
	for(int i = -p; i <= p; ++i){
		if(pre + i > 0)	dfs(pos + 1, val + pre + i, pre + i);
	}
}

int main()
{
//	freopen("math.in","r",stdin);
//	freopen("math.out","w",stdout);
	n = read(), S = read(), p = read();
//	int Minn = (S - n * (n - 1) / 2 * p) / n;
	int Maxn = (S + n * (n - 1) / 2 * p) / n;
	for(int i = 1; i <= Maxn; ++i){
//	for(int i = -29; i <= 60; ++i){
		dfs(2, i, i);
	}
	printf("%d", cnt);
	return 0;
}

T2

Problem 2 網格(grid.cpp/c/pas)code

Code

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1010;
const int INF = 1;
const int mod = 1;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};

struct node{
	int x, y, val;
};

int n, sx, sy, ex, ey;
int a[MAXN][MAXN], ans[MAXN][MAXN];
bool vis[MAXN][MAXN];
queue<node> q;

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

bool ligit(int x, int y){
	return (x > 0 && y > 0 && x <= n && y <= n);
}

void bfs(){
	q.push((node){sx, sy, 0});
	while(!q.empty()){
		node t = q.front(); q.pop();
		ans[t.x][t.y] = t.val;
		if(t.x == ex && t.y == ey) return ;
		for(int i = 0; i < 4; ++i){
			node v;
			v.x = t.x + dx[i], v.y = t.y + dy[i], v.val = t.val + 1;
			if(!vis[v.x][v.y] && ligit(v.x, v.y) && (a[v.x][v.y] == 0)){
				q.push(v);
				vis[v.x][v.y] = 1;
			}
		}
	}
}

int main()
{
//	freopen("grid.in","r",stdin);
//	freopen("grid.out","w",stdout);
	memset(ans, -1, sizeof(ans));
	n = read();
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j){
			a[i][j] = read();
		}
	}
	sx = read(), sy = read(), ex = read(), ey = read();
	bfs();
	printf("%d", ans[ex][ey]);
	return 0;
}

T3

戈蘭斜(gokigen.cpp/c/pas)ci

Solution

每一個格子只有兩種填法且 \(n \le 7\),暴力搜索兩種填法,開 \(cnt\) 數組統計鏈接個數。get

填一個格子,若是是 "\",格子左上角和右下角的 \(cnt++\),若是是 "/",格子左下角和右上角的 \(cnt++\) 。只有更改後的 \(cnt\) 小於等於目標 \(cnt\) 才繼續向下搜,不然回溯數學

發現每填一個格子,鏈接格子左上角的格點的個數就能夠被肯定,那麼只有左上角的格點個數等於目標格點個數或沒有要求才繼續搜索,不然回溯

若是搜索到第 \(n + 1\) 列時,要額外判斷邊界第 \(n + 1\) 列的 \(cnt\) 是否知足對應的個數
若是搜索到第 \(n\) 行時,要額外判斷邊界第 \(n + 1\) 行的 \(cnt\) 是否知足對應個數

如何保證無環?

不難想到,只有在填 "/" 時纔有可能出現環,那麼在填 "/" 以前,先判斷是否有環

如何處理點的座標?把行看作十位,把列看作個位就好啦

  • 算法一:考慮可撤銷並查集,然而我不會

  • 算法二:發現n很小,沒次判斷時 \(n^2\) 掃一遍建圖,在 \(dfs\)看看可否從左下角跑到右上角

  • 算法三:很顯然算法二很傻逼,直接用並查集維護就好,加完邊後判斷左下角和右上角是否在同一並查集裏,省去 \(dfs\)的時間

最後輸出方案就好啦

Code

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 10;
const int INF = 1;
const int mod = 1;

struct edge{
	int from, to, nxt;
}e[10100 << 1];
int head[MAXN * MAXN], num_edge = 0;

int n;
int go[MAXN][MAXN];
int cnt[MAXN][MAXN], now[MAXN][MAXN];
bool flag = false, Flag = false;

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

void add_edge(int from, int to){
	e[++num_edge] = (edge){from, to, head[from]}, head[from] = num_edge;
}

bool bl(int u, int fa, int end){
	for(int i = head[u]; i != -1; i = e[i].nxt){
		int v = e[i].to;
		if(v == fa) continue;
		if(v == end) {
			Flag = 1;
			return true;
		}
		bl(v, u, end);
		if(Flag) { return true;	}
	}
	return false;
}

bool pd(int sx, int sy, int ex, int ey){
	memset(head, -1, sizeof(head)); num_edge = 0;
	Flag = false;
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j){
			if(now[i][j] == 0){
				add_edge(i * 10 + j, (i + 1) * 10 + j + 1);
				add_edge((i + 1) * 10 + j + 1, i * 10 + j);
			}
			if(now[i][j] == 1){
				add_edge((i + 1) * 10 + j, i * 10 + j + 1);
				add_edge(i * 10 + j + 1, (i + 1) * 10 + j);
			}
		}
	}
	if(bl(sx * 10 + sy, 0, ex * 10 + ey)) return 1;
	else return 0;
}

void dfs(int posx, int posy){
//	cout<<posx<<" "<<posy; 
//	orz;
	if(posx == n && posy == n + 1) { 
		if( ((cnt[posx + 1][posy] == go[posx + 1][posy]) || (go[posx + 1][posy] == 9)) && ((cnt[posx][posy] == go[posx][posy]) || (go[posx][posy] == 9)) ) flag = 1; 
		return ; 
	}// 若是搜到最後一個點,說明已經找到答案。退出 
	if(posy == n + 1){ //若是這一行搜完了 
		if((cnt[posx][posy] == go[posx][posy]) || (go[posx][posy] == 9) ) posx++, posy = 1;//換行 
		else  return ;// 若是已經肯定的那個數並無達到目標,返回 
	}//
	
	now[posx][posy] = 0;// 填 "\" 
	cnt[posx][posy]++, cnt[posx + 1][posy + 1]++;// 對應位置加1 
	
	if(cnt[posx][posy] <= go[posx][posy] && cnt[posx + 1][posy + 1] <= go[posx + 1][posy + 1]) {//若是兩個對應位置大於目標位置就不向下搜索 
		if((go[posx][posy] != 9 && go[posx][posy] == cnt[posx][posy]) || (go[posx][posy] == 9)) {//若是已經肯定的那個數沒有達到目標,中止向下搜索 
			if((posx != n) || (posx == n && ( (go[posx + 1][posy] != 9 && go[posx + 1][posy] == cnt[posx + 1][posy]) || (go[posx + 1][posy] == 9) ) )){
				dfs(posx, posy + 1);//
			}
		}
	}//
	if(flag) return ;// 若是找到答案就返回 
	cnt[posx][posy]--, cnt[posx + 1][posy + 1]--;//回溯 
	
	if(pd(posx + 1, posy, posx, posy + 1)){ return ;}
	
	now[posx][posy] = 1;// 填 "/"
	cnt[posx + 1][posy]++, cnt[posx][posy + 1]++;// 對應位置加1 
	
	if(cnt[posx + 1][posy] <= go[posx + 1][posy] && cnt[posx][posy + 1] <= go[posx][posy + 1]) {//若是兩個對應位置大於目標位置就不向下搜索 
		if((go[posx][posy] != 9 && go[posx][posy] == cnt[posx][posy]) || (go[posx][posy] == 9)) {//若是已經肯定的那個數沒有達到目標,中止向下搜索 
			if((posx != n) || (posx == n && ( (go[posx + 1][posy] != 9 && go[posx + 1][posy] == cnt[posx + 1][posy]) || (go[posx + 1][posy] == 9) ) )){
				dfs(posx, posy + 1);//
			}
		}
	}
	if(flag) return ;// 若是找到答案就返回 
	now[posx][posy] = -1;//回溯 
	cnt[posx + 1][posy]--, cnt[posx][posy + 1]--;// 回溯 
}

int main()
{
//	freopen("gokigen.in","r",stdin);
//	freopen("gokigen.out","w",stdout);
	int T;
	T = read();
	while(T--){
		n = read();
		memset(now, -1, sizeof(now));
		memset(cnt, 0, sizeof(cnt));
		memset(go, 0, sizeof(go));
		flag = 0;
		char ch[10];
		for(int i = 1; i <= n + 1; ++i){
			cin>>(ch + 1);
			for(int j = 1; j <= n + 1; ++j){
				if(isdigit(ch[j])) go[i][j] = ch[j] - '0';
				else go[i][j] = 9;
			}
		}
		
		dfs(1, 1);
//		for(int i = 1; i <= n + 1; ++i){
//			for(int j = 1; j <= n + 1; ++j){
//				cout<<go[i][j]<<" ";
//			}
//			cout<<"\n";
//		}
//		cout<<"\n";
//		
//		for(int i = 1; i <= n + 1; ++i){
//			for(int j = 1; j <= n + 1; ++j){
//				cout<<cnt[i][j]<<" ";
//			}
//			cout<<"\n";
//		}
//		cout<<"\n";
		
		for(int i = 1; i <= n; ++i){
			for(int j = 1; j <= n; ++j){
				if(now[i][j] == 1) cout<<"/";
				else if(now[i][j] == 0) cout<<"\\";
				else cout<<"s";
			}
			cout<<"\n";
		} 	
	}
	return 0;
}
相關文章
相關標籤/搜索