一、连通块
1.讲解
说实话,这道题挺不好写的,作者也是看了其他大神的操作才过了。
1.首先需要创建node,包含坐标信息,并且运算符重载(方便下面move函数中的排序。还要实现旋转fz,对称(分别x,y轴)。
2.解释move函数,作用:用来判断平移是否一致,相当于把每个图形第一个点移动到了原点上。
3.len,wid函数判断我们落子后是否合法,即能通过平移旋转等操作在棋盘内,题目说了初始联通块可以在棋盘上移动,那么加点的时候就不用考虑是否在棋盘上,只用考虑加点后的图形是否可以被棋盘容纳。有一说一,真是考语文,作者原来在void dfs(vector& g,int cnt)个函数取点时加了if(xx<=0||xx>n||yy<=0||yy>m) continue;一直没过。
4.dfs加点,可以在每个点的四个方向加,也可以在加点的四个方向加。
5.dfs中的check函数,就是旋转四次,进行每次判断与原来的ans中的重复不重复。只要有一种情况重复,就return false;
6.最下面写的dfs用来判断原来图中的连通块数,>1就cout<<-1;
当然也可以用bfs。
2.代码
#include<bits/stdc++.h>
using namespace std;
const int N = 15;
int k,n,m;
int a[N][N];
int v[N][N];
int hh[]{0,1,0,-1};
int ll[]{1,0,-1,0};
struct node {
int x,y;
bool operator < (const node& n) const {
if (x!=n.x)
return x<n.x;
else
return y<n.y;
}
void fz() {
int ex = x;
int ey = y;
x = ey;
y = -ex;
}//
void fx() {
y = -y;
}
void fy() {
x = -x;
}
};
set<vector<node>> ans;
void move(vector<node>& g) {
sort(g.begin(),g.end());//排序,最后用相对位置判断平移是否可以被得到
node t =g[0];
for(int i=0;i<g.size();i++) {
g[i].x-=t.x;
g[i].y-=t.y;
}
}
int len(vector<node>& g) {
int maxi = g[0].x;
int mini = g[0].x;
for (auto it : g) {
maxi = max(maxi,it.x);
mini = min(mini, it.x);
}
return maxi - mini + 1;
}
int wid(vector<node>& g) {
int maxi = g[0].y;
int mini = g[0].y;
for (auto it : g) {
maxi = max(maxi, it.y);
mini = min(mini, it.y);
}
return maxi - mini + 1;
}
bool check(vector<node>& g) {
int l = len(g);//找到这个图的长
int r = wid(g);//找到这个图的宽
if ((l <= n && r <= m) || (l <= m && r <= n)) {
for (int i = 1;i <= 4;i++) {
for (auto& it : g) {
it.fz();
}
move(g);
if (ans.count(g)) {
return 0;
}
vector<node> gx = g;
for (auto& it : gx) {
it.fx();
}
move(gx);
if (ans.count(gx)) {
return 0;
}
vector<node> gy = g;
for (auto& it : gy) {
it.fy();
}
move(gy);
if (ans.count(gy)) {
return 0;
}
}
return 1;
}
else {
return 0;
}
}
void dfs(vector<node>& g,int cnt) {
ans.insert(g);
if(cnt==k) {//所有的点都用完了!
return;
}
set<node>s(g.begin(),g.end());//将g中的元素进入集合中.
//在这里的作用是当个标记数组用,为了判断这个位置能否落子。
for (auto &it:g) {
for (int i=0;i<4;i++) {
int xx=it.x+hh[i];
int yy=it.y+ll[i];
if (s.count({xx,yy})) {
continue;//不能落子
}
else {
vector<node> fuzhi = g;
fuzhi.push_back({xx,yy});
move(fuzhi);//先将其移动到原点位置再check
if (check(fuzhi)) {
dfs(fuzhi,cnt+1);
}
}
}
}
}
void dfs(int x,int y){
for(int i=0;i<4;i++){
int xx = x+hh[i];
int yy = y+ll[i];
if(xx<=0||xx>n||yy<=0||yy>m) continue;
if(!v[xx][yy]&&a[xx][yy]==1){
v[xx][yy]=1;
dfs(xx,yy);
}
}
}
void solve() {
cin>>k>>n>>m;
memset(a,0,sizeof(a));
memset(v,0,sizeof(v));
ans.clear();
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++) {
cin>>a[i][j];
}
}
int numblock=0;
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++) {
if (a[i][j] == 1 && !v[i][j]) {
dfs(i,j);
numblock++;
}
}
}
if (numblock>1) {
cout<<"-1"<<'\n';
return;
}
if (numblock==0) {
if (k==0) {
cout<<"0"<<'\n';
}
a[1][1] = 1;
k--;
}
vector<node> g;
for (int i=1;i<=n;i++) {
for (int j=1;j<=m;j++) {
if (a[i][j]) {
g.push_back(node{i,j});
}
}
}
move(g);//就是平移到将最小的点平移到原点(带着整个图形)
dfs(g,0);
cout<<ans.size()<<'\n';
}
signed main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int T;
cin>>T;
while(T--) {
solve();
}
return 0;
}
3.难点总结
1.图形变换
2.dfs加点
3.set判重
二、彩蛋
写了这么多难题了,写点leetcode爽一下
题目
就是道简单的小思维,给没有写出来上面难题的自己一点信心,加油!!
class Solution {
public:
int longestSubsequence(string s, int k) {
int cnt=0;
for(int i=0;i<s.size();i++){
if(s[i]=='0'){
cnt++;
}
}//所有0一定可以取到的
long long t=0,sum=0;
for(int i=s.size()-1;i>=0;i--){
if(s[i]=='1'){
sum +=(1ll<<t);
if(sum > k) break;
cnt++;
}
t++;
if(t>30){ //30次就够了,2的30次方>1e9
break;
}
}
return cnt;
}
};
没有做出来也没有关系,你我终会抵达群星。
2151

被折叠的 条评论
为什么被折叠?



