qbxt笔记——————dp
一:方法入门
可以用其他数更新自己,也可以用自己更新其他数
动态转移的顺序类似于DAG,先后算分明,没有环,不能任意更改
1. *斐波那契
1) for(int a=2;a<=n;a++)//被更新
{
f[a]=f[a-1]+f[a+2];
}
2)for(int a=0,a<=n;a++)//更新别人
{
f[a+1]+=f[a];
f[a+2]+=f[a];
}
3)int dfs(int n)//记忆化搜索
{
if(n==0)return 0;
if(n==1)return 1;
if(f[n]!=0)return f[n];
f[n]=dfs[n-2]+dfs(n-1);
return f[n];
}
2.* 杨辉三角组合数
int c[1001][1001];
int main()
{
int n,m;
cin >>n>>m;
for(int i=0;i<=n;i++)
{
c[i][0]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
cout<<c[n][m];
}
二:区间dp <o(n^3)>
合并,相邻
f[l][r]第l个到第r个合并
f[i][i]=0;f[i][i+1]=a[i]+a[i+1];
枚举断点k
f[i][j]=f[i][k]+f[k+1][j];
步骤
1 长度 2 左右端点 3 断点
1)合并石子
。。。。。。。没写
三:树形dp(喵啊)
树,最优值
f[i]:以i为根节点的树的信息
考虑目标:合并子树信息
i点的子节点个数:自己(1)+儿子的信息
用dfs
四:数位dp
*1~r个数
int sovle(int r)
{
int cnt=0;
while(r!=0)
{
cnt++;
z[cnt]=r%10;
r=r/10;
}//r的数位拆完辽
memset(f,0,sinzeof(f));
f[cnt+1][1]=1;//f[i][1]:==;f[i][0]!=
for(int a=cnt;a>=1;a--)
for(int b=0;b<=1;b++)
for(int c=0;c<=9;c++)
{
if(b==1&&c>z[a])
else
{
if(b==0)f[a][0]+=f[a+1][b];
else f[a][c==z[a]]+=f[a+1][b];
}
return f[1][1]+f[1][0];
}
}
*l<x<r的x的个数?
1~r的个数减1~x的个数
五:状压dp n<=22~24
用 v [ i ] [ j ] 来表示 i 状态下走到第 j 个地方的最小值。这里的 i 实质上是一个二进制数,每一位是 0 是 1 即表示每个地方有无去过,但是转为十进制表示状态,这便是状态压缩的基本思想。
1)*
f[0]=1;//啥也不选
for(int a=1;a<(1<<n);a++)
for(int b=0;b<n;b++)
if(((a>>b)&1)==0)f[a|(1<<b)]+=f[a];
//a的二进制的第b位:0可以选
//a的二进制第b位选(=1)
//从1多到1少枚举
2)*
【售货员的难题】*给n个点(xi,yi),要求所有的点走一遍,求最短欧拉距离
f[s][i]:状态为s,最后一个是i点。
六。博弈论dp
(一)
1.一个游戏g,两个人a,b
2.回合制,轮流操作
3.如果一个人没办法操作了,则ta输了
1)取石子
n个石子,a,b每次拿走至少一个石子,谁先不能拿谁输:先手必胜(全拿走了hiahiahia)
但是毒瘤的老师说这样麻烦。。。
出现了!dp!
f[i]:在剩下i个石子的时候,当前操作者必胜还是必败
f[0]=0;//f[0]必败态,因为没了
f[i]可以转移到f[i~0] ,如果你能转移到的状态中,有一个是必败态,则f[i]一定是必胜态:使对方必败
如果你能转移到的状态中,全是必胜态,则f[i]一定是必败态:使对方必胜
2)ex取石子
n个石头,a,b两个人
每次可以取3~5个石头
f[i]意义同1);
f[0、1、2]必败,f[3、4、5、6、7]必胜,f[8]必败;
*打表可知(n%8==0||n%8==1||n%8==2)时必败
(二)
两堆石头,每次从任意一堆中取走任一个,先手必败还是必胜?
不一定
n1!=n2必败
n1==n2必胜
f[i][j]:第一堆石子还有i个,第二堆还有j个时,当前操作者的必败/必胜态
(三)
k堆石子??
分成k个game,g1,g2.。。。。。。
先考虑一个,见(一):找后面有没有必败态。
定义sg[i]:对于一个必败态,定义它的sg值为0;
sg[i]=mex(可到达的状态)// 找到一个最小的没有出现过的自然数
sg[0]=0;sg[1]=1;sg[i]=i;
出现了!!!sg定理!!!
sg(g)=sg(g1)^sg(g2)^sg(g3)^.......^sg(gk)
必败态的sg值=0,sg=1为必胜态。
(代码。(三))
cin>>k;
for(int a=1;a<=k;a++)
{
cin>>n[a];
}
sg[0]=0;
for(int i=1;a<=233333;a++)
{
int cnt=0;
for(int b=1;b<=a;b++)
{
cnt++;
z[cnt]=sg[a-b];
}
sort(z+1,z+cnt+1);
cnt=unique(z+1,z+cnt+1)-z-1;//有多少个元素
for(int b=0;;b++)
{
if(z[b+1]!=b)
{
sg[a]=b;
break;
}
}
}
int ans=0;
for(int a=1;a<=n;a++)
ans=ans^sg[n[a]];
if(ans!=0)cout<<"xianshou";
else cout<<"houshou";
位运算
|: 1010
1110 ——>0|1=1;1|1=1‘;0|0=0
1110
&: 1010
1111 ——>0&1=0;0&0=0;1&1=1
1010
^ 1010
1110 ——>0^0=1^1=0;1^0=1;
0100
<< 001010
十进制: 10
左移: 010100 ——>a<<1《==》a*2
十进制: 20
>> 001010
十进制 10
右移 000101 ——>a>>1《==》a/2
十进制 5
两维不行就三维,三维不行就四维,四维不行就五维。。。。。总有一年,你会做出来的
——长者zhx