[CSP-S模拟测试]:装饰(状压DP)
作者:互联网
题目传送门(内部题114)
输入格式
第一行一个正整数$n$。
接下来一行$n-1$个正整数,第$i$个数为$f_{i+1}$。
接下来一行$n$个数,若第$i$个数为$0$则表示林先森希望$i$号点的彩灯是关闭状态,若第$i$个数为$1$则表示林先森希望$i$号点的彩灯是开启状态。
输出格式
输出一行一个整数,表示林先森最少需要几秒才能看到他期望看到的树。
样例
样例输入1:
4
1 2 3
0 1 1 0
样例输出1:
2
样例输入2:
7
1 1 2 2 3 3
0 1 1 1 0 0 1
样例输出2:
3
数据范围与提示
样例解释:
第一个样例:
$\bullet$第一秒林先森拨动$3$号点的开关,彩灯状态变为$0\ 0\ 1\ 0$。
$\bullet$第二秒林先森什么也不做,彩灯状态变为$0\ 1\ 1\ 0$,满足要求。
第二个样例:
$\bullet$第一秒林先森拨动$4$号点的开关,彩灯状态变为$0\ 0\ 0\ 1\ 0\ 0\ 0$。
$\bullet$第二秒林先森拨动$7$号点的开关,彩灯状态变为$0\ 1\ 0\ 1\ 0\ 0\ 1$。
$\bullet$第三秒林先森拨动$1$号点的开关,与$2$号点传递的拨动效果抵消,彩灯状态变为$0\ 1\ 1\ 1\ 0\ 0\ 1$,满足要求。
数据范围:
对于$30\%$的数据,$n\leqslant 8$。
对于另$30\%$的数据,林先森希望看到所有彩灯都被点亮。
对于$100\%$的数据,$1\leqslant n\leqslant 16$。
题解
数据范围较小,考虑状压$DP$。
因为答案最大为$n$,所以我们可以枚举秒数。
设$dp[i][state]$表示第$i$秒,状态为$state$是否可行。
因为开关只与正负是否相同有关,而与具体是开还是关无关,所以我们可以假设最终的状态为起始状态,然后想办法转移到$0$状态(其实就是代码复杂度低一点而已)。
预处理出来每个点会对其它的点在下一秒做的贡献即可。
时间复杂度:$\Theta(2^nn^2)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h> using namespace std; int n,state; int fa[20],Map[20][20]; bool dp[20][100000]; int main() { scanf("%d",&n); for(int i=0;i<=n;i++)Map[1][i]=1; for(int i=2;i<=n;i++) { Map[i][0]=1<<(i-1); scanf("%d",&fa[i]); int now=i; for(int j=1;j<=n;j++) { now=fa[now]; if(now)Map[i][j]=Map[i][j-1]|(1<<(now-1)); else Map[i][j]=Map[i][j-1]; } } for(int i=1;i<=n;i++){int x;scanf("%d",&x);state|=x<<(i-1);} dp[0][state]=1; for(int i=0;i<=n;i++) { if(dp[i][0]){printf("%d",i);break;} for(int s=1;s<(1<<n);s++) if(dp[i][s]) { dp[i+1][s]=1; for(int j=1;j<=n;j++) dp[i+1][s^Map[j][i]]=1; } } return 0; }
rp++
标签:状态,bullet,林先森,状压,样例,号点,彩灯,CSP,DP 来源: https://www.cnblogs.com/wzc521/p/11782687.html