20210103搜索专场
写在前面
三道题全是搜索,前两道傻逼题,T3码量巨长
期望 \(AK\) ,实际得分 \(60 + 100 + 65 = 225pts\)
读错题的都是士兵
T1
Solution
数据都很小,暴力搜索即可
注意枚举第一个数可取的范围,注意所有数都必须是正整数
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
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
Solution
每个格子只有两种填法且 \(n \le 7\),暴力搜索两种填法,开 \(cnt\) 数组统计连接个数。
填一个格子,如果是 "\",格子左上角和右下角的 \(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;
}