2022.3.17
蓝书
AcWing 185. 玛雅游戏
思路:由于n<=5,可以暴力搜索,但是要模拟很多过程非常麻烦,当三个或以上的相同颜色的块连在一起的时候得把他们同时删去。同时还得判断是否能够构成方案,如果最后有任意一个颜色的块的数量为<=2说明肯定不能连成三个以上的了,这时就说明无解。首先读入的时候将所有有颜色的块的和以及它们分别和都求出来。在dfs的时候判断当前是想左还是向右,然后进行交换。在更新状态的时候需要吧悬空的块都让他落下来,并且还要查看哪些块是连在一起的然后将他们删除,此时还需要更新总和和各自的和。记得还要备份和还原现场。
写了一上午还是没有写出来,最后看了题解。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=10+10,INF=1e8;
int n,mp[N][N], bmp[N][N][N];
int cnt[N], bcnt[N][N],vis[N][N];
struct node
{
int x, y, op;
} ans[N];
bool check()
{
for (int i = 1; i <= 10; i++)
if (cnt[i] == 1 || cnt[i] == 2)
return 0;
return 1;
}
void drop()
{
for (int i = 0; i < 5; i++)
{
int k = 0;
for (int j = 0; j < 7; j++)
if (mp[i][j])
mp[i][k++] = mp[i][j];
while (k < 7) mp[i][k++] = 0;
}
}
bool remove()
{
int f=0;
memset(vis, 0, sizeof vis);
for (int x = 0; x < 5; x ++ )
{
for (int y = 0; y < 7; y ++ )
if (mp[x][y])
{
int l = x, r = x;
while (l - 1 >= 0 && mp[l - 1][y] == mp[x][y]) l--;
while (r + 1 < 5 && mp[r + 1][y] == mp[x][y]) r++;
if (r - l + 1 >= 3)
{
f = 1;
vis[x][y] = 1;
}
else
{
l = r = y;
while (l - 1 >= 0 && mp[x][l - 1] == mp[x][y]) l--;
while (r + 1 < 7 && mp[x][r + 1] == mp[x][y]) r++;
if (r - l + 1 >= 3)
{
f = 1;
vis[x][y] = 1;
}
}
}
}
if (f)
{
for (int x = 0; x < 5; x ++ )
for (int y = 0; y < 7; y ++ )
if (vis[x][y])
{
cnt[0] --;
cnt[mp[x][y]] --;
mp[x][y] = 0;
}
}
return f;
}
void update()
{
while(1)
{
drop();
if(!remove())
break;
}
}
bool dfs(int dep)
{
if (dep == n) return !cnt[0];
if(!check())
return 0;
memcpy(bmp[dep], mp, sizeof mp);
memcpy(bcnt[dep], cnt, sizeof cnt);
for (int x = 0; x < 5; x ++ )
for (int y = 0; y < 7; y ++ )
if (mp[x][y])
{
int xx = x + 1;
if (xx < 5)
{
ans[dep] = { x, y, 1 };
swap(mp[x][y], mp[xx][y]);
update();
if (dfs(dep + 1)) return 1;
memcpy(mp, bmp[dep], sizeof mp);
memcpy(cnt, bcnt[dep], sizeof cnt);
}
xx = x - 1;
if (xx >= 0 && mp[xx][y]==0)
{
ans[dep] = { x, y, -1 };
swap(mp[x][y], mp[xx][y]);
update();
if (dfs(dep + 1)) return 1;
memcpy(mp, bmp[dep], sizeof mp);
memcpy(cnt, bcnt[dep], sizeof cnt);
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for (int i = 0; i < 5;i++)
{
int x,j=0;
while(cin>>x&&x)
{
cnt[0]++;
cnt[x]++;
mp[i][j++] = x;
}
}
if (dfs(0))
{
for (int i = 0; i < n; i++)
cout<<ans[i].x<<' '<<ans[i].y<<' '<< ans[i].op<<'\n';
}
else
cout << -1 << '\n';
return 0;
}
AcWing 184. 虫食算
一开始想的是由字典序从小到大枚举每一个字母,结果超时了,看了题解发现需要按照题目输入的字符串从地位到高位枚举,dfs枚举n进制的每一个数从字符串的最低位枚举到最高位,先判断一下当前这一位数有没有用过,如果没有则将这位当成当前这个字母的n进制赋值。当当前这一列的字母已经确定但但进位还没有确定的时候我们枚举该列是进位还是不进位,如果两种情况都符合的话剪枝然后在判断一下当前这一列是最后一列的情况,如果两个数加起来已经大于等于n的话剪枝,如果进位已经确定,那么如果当前两个数加上进位不等于第三个数的话剪枝,如果是最高位则大于等于n的时候剪枝,同时还需要把进位给更新一下。改完过后还是发现超时,看了代码发现输入也有问题,试了好几次输入都是超时,最后改了个和题解差不多的输入才能过,应该是循环次数太多了。其实最好还是题解那样,每次把每一列的三个字母都读进来。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=26+10,INF=1e8;
char a[N], b[N], c[N];
int ans[N], vis[N],d[N],n;
bool check()
{
int t = 0;
for (int i = n - 1; i >= 0;i--)
{
int aa = ans[a[i] - 'A'];
int bb = ans[b[i] - 'A'];
int cc = ans[c[i] - 'A'];
if(aa!=-1&&bb!=-1&&cc!=-1)
{
if(t==-1)
{
if((aa+bb)%n!=cc&&(aa+bb+1)%n!=cc)
return 0;
if(i==0)
{
if(aa+bb+1>=n||(aa+bb)>=n)
return 0;
}
}
else
{
if((aa+bb+t)%n!=cc)
return 0;
if(i==0)
{
if((aa+bb+t)>=n)
return 0;
}
t = (aa + bb + t) / n;
}
}
else
t = -1;
}
return 1;
}
bool dfs(int x)
{
if(x==n)
return 1;
for (int i = 0; i < n;i++)
{
if(vis[i])
continue;
vis[i] = 1;
ans[d[x]] = i;
if(check()&&dfs(x+1))
return 1;
vis[i] = 0;
ans[d[x]] = -1;
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
memset(ans, -1, sizeof ans);
cin >> a >> b >> c;
int cnt = 0;
for (int i = n-1; i >= 0;i--)
{
if(!vis[a[i]-'A'])
{
d[cnt++] = a[i] - 'A';
vis[a[i]-'A'] = 1;
}
if(!vis[b[i]-'A'])
{
d[cnt++] = b[i] - 'A';
vis[b[i]-'A'] = 1;
}
if(!vis[c[i]-'A'])
{
d[cnt++] = c[i] - 'A';
vis[c[i]-'A'] = 1;
}
}
memset(vis, 0, sizeof vis);
dfs(0);
for (int i = 0; i < n;i++)
{
cout << ans[i] << ' ';
}
return 0;
}
AcWing 183. 靶形数独
思路:为了尽量高分,我们要在dfs的时候不断更最大值,为了方便可以先打表,把分数都先打上,然后读入数据,玩过数独的都知道得先找0比较少的那个宫先填上,对于没填的点枚举合法的点填上,同时更新行列和宫的状态,然后dfs下一个点,到最后输出最大值。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=20,INF=1e8;
int mp[N][N],row[N][N], col[N][N], cell[N][N],zero[N],rnk[N],ans;
int db[9][9]{
{6, 6, 6, 6, 6, 6, 6, 6, 6},
{6, 7, 7, 7, 7, 7, 7, 7, 6},
{6, 7, 8, 8, 8, 8, 8, 7, 6},
{6, 7, 8, 9, 9, 9, 8, 7, 6},
{6, 7, 8, 9, 10, 9, 8, 7, 6},
{6, 7, 8, 9, 9, 9, 8, 7, 6},
{6, 7, 8, 8, 8, 8, 8, 7, 6},
{6, 7, 7, 7, 7, 7, 7, 7, 6},
{6, 6, 6, 6, 6, 6, 6, 6, 6},
};
int getcell(int x,int y)
{
return x / 3 * 3 + y / 3;
}
bool cmp(int a,int b)
{
return zero[a] < zero[b];
}
void dfs(int cnt,int sum)
{
if(cnt==81)
{
ans = max(ans, sum);
return;
}
int x = rnk[cnt/9], y = cnt % 9, ge = getcell(x, y);
if(mp[x][y])
dfs(cnt + 1, sum+db[x][y]*mp[x][y]);
else
{
for (int i = 1; i <= 9;i++)
{
if(!row[x][i]&&!col[y][i]&&!cell[ge][i])
{
row[x][i] = col[y][i] = cell[ge][i] = 1;
dfs(cnt + 1, sum + db[x][y] * i);
row[x][i] = col[y][i] = cell[ge][i] = 0;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
for (int i = 0; i < 9;i++)
{
rnk[i] = i;
for (int j = 0; j < 9;j++)
{
cin >> mp[i][j];
if(mp[i][j])
{
row[i][mp[i][j]] = col[j][mp[i][j]] = cell[getcell(i, j)][mp[i][j]] = 1;
}
else zero[i]++;
}
}
sort(rnk, rnk + 9,cmp);
dfs(0, 0);
if(ans)
cout << ans << '\n';
else
cout << -1 << '\n';
return 0;
}