机房测试:decoration(树+状压)

题目:

 

 分析:

这道题的难点在于:

每一次转移的时候,要记录很多被翻转的点,还要确定他们下一次的fa是谁。

所以转换一下思路:从终止状态转移到0状态(初始状态),倒着来。

假设现在到了倒数第 i 秒,那么从第i~ans的时间内翻转了一个灯他是一直要向上跳并影响其他灯的,跳的时间为i,所以可以预处理一下:fa[i][j]表示i灯在跳j步后能变化到的状态

每一次转移的时候,只需要枚举灯,异或上fa数组即可快速知道倒数第i+1秒的状态。

某一次更新到了初始状态0,就直接输出现在是倒数第几秒即可。

因为还要记录时间,所以dp要开两维。

注意:如果一个点已经传递到了根节点,那么状态就不会变了!!

#include<bits/stdc++.h>
using namespace std;
#define N 18
#define ri register int
int ff[N],fa[N][N],dp[N][1<<N],n,goal=0,x;
int main()
{
    freopen("decoration.in","r",stdin);
    freopen("decoration.out","w",stdout);
    scanf("%d",&n);
    ff[1]=0;
    for(ri i=2;i<=n;++i) scanf("%d",&x),ff[i]=x;
    for(ri i=1;i<=n;++i) scanf("%d",&x),goal<<=1,goal+=x;
    for(ri i=1;i<=n;++i){
        fa[i][0]=(1<<(n-i)); 
        int pre=ff[i];
        for(ri j=1;j<=n;++j){
            if(pre) fa[i][j]=fa[i][j-1]^(1<<(n-pre));//如果说已经到达根节点1了,就不用再跳了 
            else fa[i][j]=fa[i][j-1];
            pre=ff[pre];
        }
    }
    dp[0][goal]=1;
    for(ri t=0;t<=n;++t){
        if(dp[t][0]) { printf("%d
",t); return 0; }
        for(ri j=0;j<(1<<n);++j)
        if(dp[t][j]){
            dp[t+1][j]=1;
            for(ri k=1;k<=n;++k)
            dp[t+1][j^(fa[k][t])]=1;
        }
    }
    return 0;
}
/*
7
1 1 2 2 3 3
0 1 1 1 0 0 1
3

7
1 1 2 2 3 3
1 1 1 1 0 0 1
3

8
1 1 2 4 2 4 6
0 0 1 0 1 1 0 1


3 
1 1
0 1 1
*/
View Code
原文地址:https://www.cnblogs.com/mowanying/p/11794052.html