P4906 小奔关闹钟
Link.
PS.
暂无
Problem.
- 目标:关闭所有的电灯
- 特殊:关了一个点灯之后,会影响其他电灯的开闭,其他点灯的开闭又继续影响
- 限制:上面的影响只会持续2轮,
不要问我为什么,我是从样例中看出来的QwQSolution.
由于题意分析中的第三条,我们可以考虑初始化更改一个点灯的状态后会产生的影响。
复杂度是$\texttt{O(n}^\texttt{3}\texttt{)}$,对于$\texttt{1<n<20}$的数据一定能过。
然后就可以暴力$\texttt{dfs}$来求答案了。
当然,如果两次或多次关同一盏灯,那么开关的效果会被抵消,所以对于每一个点,只有动与不动两种情况。
所以$\texttt{dfs}$的复杂度是$\texttt{O(2}^\texttt{n}\texttt{)}$,能过。Attention.
由于$\texttt{n<=20}$,$\texttt{2}^\texttt{20}=1048576$,在$\texttt{int}$的数据范围内,所以我们可以考虑状压。
由于开灯与关灯具有如下性质:$\texttt{0+0=0,1+1=0,0+1=1,1+0=0}$,所以我们可以把它当成异或操作。Coding.
代码里有解释1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using namespace std;
const int N=25,INF=1000000007;
int n,m,ans,cha[N];
char a[N][N];
inline int dfs(int x,int s)
{
if(x==n+1) {if(s==0) return 0;return INF;} //递归的边界
return min(dfs(x+1,s),dfs(x+1,s^cha[x])+1); //枚举这一盏灯动不动
}
inline void init() //读入函数,没什么好解释的把
{
scanf("%d",&n);
memset(a,0,sizeof(a));
for(int i=1,t,x;i<=n;i++)
for(scanf("%d",&t);t--;)
scanf("%d",&x),a[i][x]=1;
}
inline void ready() //前面说的预处理部分
{
memset(cha,0,sizeof(cha)); //cha代表change,表示动一下这个开关,会产生的影响的状态(已压缩
for(int i=1;i<=n;i++)
{
cha[i]^=ya(i-1); //它本身状态改变
for(int j=1;j<=n;j++) //持续的第一轮
if(go(i,j))
{
cha[i]^=ya(j-1);
for(int k=1;k<=n;k++) if(go(j,k)) cha[i]^=ya(k-1); //它持续的第二轮
}
}
ans=dfs(1,ya(n)-1); //找到答案
}
int main()
{
return init(),ready(),ans<=INF?printf("%d\n",ans):puts("Change an alarm clock,please!"),0; //一行的主程序完美地结束
}