HDU6809 Head Maker 题解
题目描述
\(T\) 组数据,\(n\times m\) 的网格图中部分格子被挖掉了,保证剩下的格子连通。
问剩下的格子能否折成一个正方体,每个面允许被原先的多个格子覆盖。
数据范围
- \(1\le T\le 100,1\le n,m\le 6\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
将相邻的格子连边,会形成一张无向连通图。
为折成正方体形状,我们需要断开一些边,形成一棵生成树,每条树边都是一道折痕。
模拟翻折过程过于复杂,考虑一个更高效的check方式:
将正方体六个面染上不同颜色,在网格图上沿生成树的边滚动,如果所有颜色在俯视图中都出现过则有解。
如果格子数量很多,那么大概率每一棵生成树都有解。
如果格子数量很少,那么它的生成树数量也不会很多。
考虑随机化。
随机 \(10^4\) 棵生成树,用上述方法判断是否有解。
然而原题好像随机 \(10\) 次就能过。
时间复杂度 \(\mathcal O(T\cdot 10^4\cdot nm)\) 。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=40;
int k,m,n,t;
int f[maxn],id[8][8];
char s[8];
vector<pii> g[maxn];
bitset<6> vis;
mt19937 rnd(time(0));
struct edge
{
int u,v,dir;
};
vector<edge> e;
int find(int x)
{
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
void dfs(int x,int fa,int l,int r,int d,int u,int f,int b)
{
vis[u]=1;
for(auto p:g[x])
{
int v=p.fi,dir=p.se;
if(v==fa) continue;
if(dir==0) dfs(v,x,l,r,b,f,d,u);
if(dir==1) dfs(v,x,l,r,f,b,u,d);
if(dir==2) dfs(v,x,u,d,l,r,f,b);
if(dir==3) dfs(v,x,d,u,r,l,f,b);
}
}
bool check()
{
for(int i=1;i<=10000;i++)
{
shuffle(e.begin(),e.end(),rnd);
for(int j=1;j<=k;j++) f[j]=j,g[j].clear();
for(auto p:e)
{
int u=p.u,v=p.v,dir=p.dir;
if(find(u)==find(v)) continue;
f[find(u)]=find(v),g[u].push_back(mp(v,dir)),g[v].push_back(mp(u,dir^1));
}
vis.reset(),dfs(1,0,0,1,2,3,4,5);
if(vis.all()) return true;
}
return false;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(id,k=0,sizeof(id)),e.clear();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++) id[i][j]=s[j]=='1'?++k:0;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(!id[i][j]) continue;
if(id[i-1][j]) e.push_back({id[i][j],id[i-1][j],0});
if(id[i][j-1]) e.push_back({id[i][j],id[i][j-1],2});
}
printf(check()?"Yes\n":"No\n");
}
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17389230.html
浙公网安备 33010602011771号