y总字幕版注释,超详细
第三篇y总字幕题解,大家慢用~~
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;//应该是溢出了对吧,所以我们要把所有的f改成long long
const int N=12;//待会儿我会给大家解释一下为什么要开到12
const int M=1<<10,K=110;//K是我们的国王数量
int n,m;//这里用m来表示国王数量,因为我习惯用n来表示一个数然后m来表示另外一个值
vector<int> state; //state 用来表示所有的合法的状态
int id[M];//id的话是存这个每一个状态和这个它的下标之间的映射关系 id有用到吗?好像没有用到
//应该是我之前写的时候思路不太一样,想的时候可能,就是前面设计的思路和实际用到的思路可能会有一点区别
//所以这里其实是不要加id i 的
vector<int> head[M];//这个是每一状态所有可以转移到的其他状态
int cnt[M];/*然后cnt的话存的是每个状态里面 1 的个数,因为我们刚才我们的状态转移方程里面,
其实有一个需要求这个每一个状态里面1的个数的一个过程对吧*/
LL f[N][K][M];//这就是我们刚才说的那个状态表示
bool check(int state)
{
for(int i=0;i<n;i++)
if((state >> i & 1)&&(state >> i + 1 & 1))
return false;//如果存在连续两个1的话就不合法
return true;//否则的话就是合法的
}
int count(int state)//这里y总没具体解释,我补充一下,这里就是计算某个数二进制里面1的个数
{
int res=0;
for(int i=0;i<n;i++)res+=state>>i&1;
return res;
}
int main()
{
cin>>n>>m;
//首先我们需要把所有合法状态处理出来对吧,把它预处理一下
for(int i=0;i<1<<n;i++)
if(check(i))
/*check函数是检查一下我们当前状态是不是合法,也就是检查一下我们这个状态里面是不是存在连续的两个1,
如果不存在的话就表示它是合法的*/
{
state.push_back(i);
id[i]=state.size()-1;//id存的是我们这个合法状态对应的下标是多少
cnt[i]=count(i);//cnt的话存的是这个i里面 1 的个数是多少
}
//然后我们来看一下这个不同状态之间的一个这个边的关系
for(int i = 0;i< state.size();i ++ )
for(int j=0;j<state.size();j++)
{
//用a来表示第一个状态,用b来表示第二个状态
int a=state[i],b=state[j];
//这里是建立一个不同状态之间的转移关系
//先预处理一下哪些状态和哪些状态之间可以转移
//首先转移的话是要满足两个条件对吧
//一个是这个 a 和 b 的交集必须要是空集,它必须是空集才可以,否则的话同一列的两个国王会攻击到
//并且的话它们的这个这个并集的话也是需要去满足我们不能包含两个相邻的1的
if((a&b)==0&&check(a|b))
// head[a].push_back(b);
//然后只要这个 b 是合法的,那么我们就把 b 添加到 a 可以转移到的状态集合里面去
//这里y总第一次写错了,debug了
head[i].push_back(j);
//这里是debug过程
//这里写错了,这里应该是head[i].push_back(j);
//因为咱么这里去做的时候用的是下标,不是一个状态的值
}
//好,那剩下的就是 DP 了对吧
f[0][0][0]=1;
//最开始的时候,我们f[0][0][0]=1
/*什么意思呢,就是说,前0行对吧,我们前0行已经摆完了,其实也就是一行也没有摆的情况下,
那么此时由于我们这个是在棋盘外面,
所以肯定每个国王都不能摆对吧,所以我们就只有0这个状态时合法的,那么这个状态的方案数是1*/
//好,然后从前往后枚举每一种状态
for(int i=1;i<=n+1;i++)
for(int j=0;j<=m;j++)//j的话是从0到m对吧,m表示的是国王数量
for(int a=0;a<state.size();a++)//然后我们来枚举一下所有的状态,a表示第i行的状态
for(int b : head[a])//然后来枚举所有a能到的状态
{
//这里要判断一下
//首先要判断的是
//求一下我们a里面的一个1的个数对吧
int c=cnt[state[a]];
//好,然后如果说,呃,就我们的j必须要大于等于c对吧,j是必须要大于等于c的
//为什么呢,因为我们这个表示我们当前这行摆放的国王数量一定要小于等于我们整个的上限对吧
if(j>=c)//如果数说满足要求的话,那么我们就可以转移了
{
f[i][j][a]+=f[i-1][j-c][b];
//转移的话就是f[i][j][a]+=f[i-1][j-c][b],然后从b转移过来
}
}
//好,那我们最终的答案是什么呢?
//我们的最终的答案应该是这个f[n][m][ ],然后最后一维可以直接去枚举对不对
//去枚举一下最后一维是从,就是所有合法状态都是可以的,就最后一行它所有合法状态都是可以的对不对
//那这里的话我们可以偷个懒,不是偷个懒,我们可以有个小技巧,就是我们在枚举i的时候,枚举到n+1就可以了
//就是我们去算到第i+1行,假设我们的棋盘是一个n+1 * n的一个棋盘,多了一行
/*那么我们最终算的时候 就需要输出一个 f[n+1],就是第n+1行,
一共摆到了第n+1行,然后m,然后0,因为第n+1行一个都没摆,对吧*/
cout<<f[n+1][m][0]<<endl;
/*就是我们假设存在第n+1行,但是第n+1行完全没有摆,
那这种情况里面的所有方案其实就是等于这个这个我们只有n行的一个所有方案,对吧*/
/*那这样枚举n+1的一个好处是我们最后不需要再循环枚举最后一行的状态了,
就是我们这个f[n+1][m][0]已经在这个循环里面被循环算出来了*/
//所以可以少一层循环
/*这里的话就是为什么我们一开始N要从12开始,对吧,首先我们要用到11这个下标对吧,
那其实11这个下标是需要开长度是12才可以*/
return 0;
}
我们不写题解,我们只是y总的搬运工~~
dxls的吧0.0
你是真的nb
hh,tql。我还特意再听一遍y总这部分,对照一下
66666666太清晰拉
666
666~
666666666666666666666666666666666666666666666
牛逼!
赞
感谢啦~~~~
逆天!id 数组的解释都带着。hhhhhh
你是魔鬼吧
for (int i = 0; i < state.size(); i )
for (int j = i; j < state.size(); j )
{
int a = state[i], b = state[j];
if ((a & b) == 0 && check(a | b))
head[i].push_back(j), head[j].push_back(i);
}
为什么这样子写,答案是错的??
在我的理解里,如果a->b那么肯定可以b->a呀,这里卡了我好久
重复了
第i层<总国王数,那为什么没有判断head里面有没有大于总国王数呢?万一a+b>总国王数了咋整?
第i层<总国王数,那为什么没有判断head里面有没有大于总国王数呢?万一a+b>总国王数了咋整?
if((a&b)==0&&check(a|b)) 这里为啥还要再check一次啊,之前的每个state不是都check过了吗,这里的a,b应该都满足没有两个国王啊
这里check是检查上下两行有没有相邻的,因为国王能攻击到周围八个,如果你左上角有国王也不行
哦哦,懂了,感谢^ω^
这里的代码如果说第三层循环是枚举在第i行时的合法状态的下标,那第四层枚举的是啥?
是能够被以a为下标的状态转移的状态下标,还是能够被以a为下标的状态转移的具体状态?
这块有点没看懂
第四层枚举的是下标,head数组里存的是能转移的下标
为什么j从零开始枚举?国王的数量为什么从零开始枚举呢?
下标那里也可以用状态的
农夫山泉的宣传语改的吧!!!