题解 P2383 【狗哥玩木棒】
这道题看似是
\rm DFS ,但是可以用\rm BFS 做——套用另一位某大
^D 法^F 师^S \frak{Phaethon}\ \Omega 的激情句式
前言
\rm{STL}\bold{\text{大法好!}}
思路
由于一个无趣的赌注以及对
大体思路(第一次尝试)
一看到本题,粗略的思路是使用结构体(当然最后没有用)来存放每一种木棒摆放状态:yes。如果队列空了也没有被填满,那就输出no。
一个非常明显的优化是预判断木棒能不能组成一个正方形,只需要提前求出木棒总长模
然后是实现。
定义是这样的:
#define pr4 pair<pair<int,int>,pair<int,int> > //四条边
#define stix pair<pr4,int> //四条边及最后木棒编号
所以,正方形的四条边分别是t.first.first.first,t.first.first.second,以此类推。
最后是代码。核心
bool b=false; //b:是否已完成
while(!q.empty) //q:BFS队列
{
stix t=q.front(); //t:队首
int v=t.second+1; //v:需要添加的木棒编号,a数组用以存储木棒
//第一、二条边
if(t.边+a[v]<=s) //s:边长
{
stix l=t; //l:临时状态
l.边+=a[v]; //添加木棒
++l.second; //更新木棒编号
if(win(l,s)) //如果已经填满每条边
{
cout<<"yes\n";
b=true;
break; //跳出搜索
}
q.push(l); //状态入队
}
//第三、四条边
if(t.前一条边&&t.边+a[v]<=s)
//↑=t.前一条边!=0,即前一条边已使用
{
↑↑↑idem↑↑↑
}
q.pop(); //标准出队
}
if(!b)
cout<<"no\n";
优化一(第二次尝试)
当我们满怀期待地把上面代码提交上去时,我们发现——
这不奇怪,
第一个优化思路是,先将
if(t.边+a[v]==s||(t.边+a[v]+a[v+1]<=s))
...
优化二(第三次尝试)
然而这个优化似乎对
通过几次数据撞击(为什么不公开数据……?),很可能数据中木棒长度重复次数相当多,这时我们的去重优化就不管用了。怎么办?上
(关于insert()和count()操作就足够了。
我们先定义一个以stix状态为索引的
if(!r.count(l)) //r:判重set
{
q.push(l);
r.insert(l);
}
然后呢?然后就
代码实现
由于前面讲的够多了,那么不如直接上完整代码了:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#define pr4 pair<pair<int,int>,pair<int,int> >
#define stix pair<pr4,int>
using namespace std;
queue<stix> q;
set<stix> r;
int sread() //慢读优化(?
{
int a;
cin>>a;
return a;
}
stix crea(int a,int b,int c,int d,int i) //构造一个stix状态,省代码
{
return make_pair(make_pair(make_pair(a,b),make_pair(c,d)),i);
}
bool win(stix t,int s) //判断t状态下四条边(边长为s)是否已经填满
{
return t.first.first.first==s&&t.first.first.second==s&&t.first.second.first==s&&t.first.second.second==s;
}
int main()
{
ios::sync_with_stdio(0);
int Phaethon_is_a_dalao=sread(); //读入测试组数
while(Phaethon_is_a_dalao--)
{
while(!q.empty()) q.pop(); //每次清空BFS队列
bool b=false;
int n=sread(),s=0; //n:木棒数
//这里先把s作为木棒总长的计数,之后再/4
int a[30];
for(int i=0;i<n;++i)
{
a[i]=sread();
s+=a[i];
}
if(s%4!=0) //判断是否能组成一个正方形
{
cout<<"no\n";
continue;
}
s/=4;
sort(a,a+n);
q.push(crea(a[0],0,0,0,0));
r.insert(crea(a[0],0,0,0,0)); //先将初状态入队、入set
while(!q.empty())
{
stix t=q.front();
int v=t.second+1;
if(t.first.first.first+a[v]==s||(v<n-1&&t.first.first.first+a[v]+a[v+1]<=s))
{
stix l=t;
l.first.first.first+=a[v];
++l.second;
if(win(l,s))
{
cout<<"yes\n";
b=true;
break;
}
if(!r.count(l))
{
q.push(l);
r.insert(l);
}
}
if(t.first.first.second+a[v]==s||(v<n-1&&t.first.first.second+a[v]+a[v+1]<=s))
{
stix l=t;
l.first.first.second+=a[v];
++l.second;
if(win(l,s))
{
cout<<"yes\n";
b=true;
break;
}
if(!r.count(l))
{
q.push(l);
r.insert(l);
}
}
if(t.first.first.second&&(t.first.second.first+a[v]==s||(v<n-1&&t.first.second.first+a[v]+a[v+1]<=s)))
{
stix l=t;
l.first.second.first+=a[v];
++l.second;
if(win(l,s))
{
cout<<"yes\n";
b=true;
break;
}
if(!r.count(l))
{
q.push(l);
r.insert(l);
}
}
if(t.first.second.first&&(t.first.second.second+a[v]==s||(v<n-1&&t.first.second.second+a[v]+a[v+1]<=s)))
{
stix l=t;
l.first.second.second+=a[v];
++l.second;
if(win(l,s))
{
cout<<"yes\n";
b=true;
break;
}
if(!r.count(l))
{
q.push(l);
r.insert(l);
}
}
q.pop();
}
if(!b)
cout<<"no\n";
}
return 0;
}
附言
-
- 虽然看似我只试了三次就出了正解,但实际上……看评测记录吧(笑)这道题硬要钦点
\rm BFS 来做还是相当伤脑的……。 -
-