台阶问题
n,k 台阶每次最多可以跨k层,最少1层,问到第n个台阶的方案数
很明显,到第n个台阶的方案数,就是到前面所有的n-k个台阶的 方案数之和。
题目有要求取模,那就每次求得并取模。
复杂度 nk,题目数据 n≤100000,K≤100,还可以凑合
#include
using namespace std;
const int MOD=100003;
const int maxn=100005;
int a[maxn]={0};
int main()
{
int n,k;
cin>>n>>k;
a[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)
{
if(i-j<0)break;
a[i]+=a[i-j];
a[i]%=MOD;
}
}
cout<
数的划分
n,k,问将n划分为k个数相加的方案数(组合数)
抽象为小球问题:
一共n个小球,放到k个桶里,放法有哪些?
1.至少有一个桶里有一个球;
2.每个桶都不止一个球;
两种情况相加,就是所有的方案。
对于1:那问题就和 【n-1个球放到k-1个桶里】 等价
对于2:每个桶都不知一个球,那我把每个桶都拿走一个球,之和再放回去就行
那问题就和 【把n-k个球放到k个桶里】等价
于是我们就定义,a[n][k]为n个球放入k个桶里的方案数
我们从小到大 递推即可。
#include
using namespace std;
int n,k,f[201][7]={0}; //f[k][x] k 分成 x 份 ={f[k-1][x-1],f[k-x][x]}
int main()
{
cin >> n >> k;
for(int i=1;i<=n;i++)
{
f[i][1]=1;
f[i][0]=1;
}
for(int i=2;i<=n;i++)
{
for (int x=2;x<=k;x++)
{
if (i>x) f[i][x]=f[i-1][x-1]+f[i-x][x];
else f[i][x]=f[i-1][x-1];
}
}
cout<
传球游戏
n个人围一圈,开始球在1号手里,问经过m次传球,回到1号手中的方案数。球只能给相邻的两个人。
所以我们定义数组 a[i][k]表示 第i个人在第k次传球得到球的方案数
第i个人,[1-n] 一般的a[i][k]=第k-1次 球在左边那个人+ 球在右边那个人手中 的方案数
即: a[i][k]=a[i-1][k-1]+a[i+1][k-1],在1和n号 处理一下。
我们已知起始是在1号手里的,于是a[1][0]=1;
#include
using namespace std;
const int maxn=33;
int a[maxn][maxn]={0};
int main()
{
int n,m;
cin>>n>>m;
a[1][0]=1;
for(int k=1;k<=m;k++)
{
a[1][k]=a[2][k-1]+a[n][k-1];
for(int i=2;i
奇怪的电梯
第i层电梯只可以向上或者向下ki,所到层数要求合法,否则不让去。
一共n层,问从A层到B层,最少需要乘坐电梯的次数。
思路:bfs 去过的楼层标记一下,不要重复去
#include
using namespace std;
int a[205],vis[205];
int n,A,B;
struct node
{
int lev,step;
}s,now;
int bfs()
{
queueq;
s.lev=A,s.step=0;
vis[A]=1;
q.push(s);
while(!q.empty())
{
now=q.front();q.pop();
if(now.lev==B)
{
cout<=1)
{
s.lev=levv-a[levv];
s.step=now.step+1;
if(!vis[s.lev])
{
vis[s.lev]=1;
q.push(s);
}
}
}
return -1;
}
int main()
{
cin>>n>>A>>B;
for(int i=1;i<=n;i++) cin>>a[i];
memset(vis,0,sizeof(vis));
if(bfs()==-1) cout<<"-1";
}
数字三角形
给出n和一个n层的三角形,找出一条路径,使得和最大。每个点只能向左下或者向右下 走。
思路:从下往上 将较大的那个数加上去,依次递推
#include
using namespace std;
const int maxn=1005;
int a[maxn][maxn]={0};
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
cin>>a[i][j];
}
}
for(int i=n-1;i>=1;i--)
{
for(int j=1;j<=i;j++)
{
a[i][j]+=max(a[i+1][j],a[i+1][j+1]);
}
}
cout<
数列分段
将数列分为m段,求最小的 每段的和的最大值。
思路:二分+贪心
#include
using namespace std;
const int maxn=100005;
int n,m,a[maxn];
int l,r=0,mid;
inline bool checkk(int x)//将数列分为每段和都<=x
{
int ans=0,num=0;//ans为该段和,num为段数
for(int i=1;i<=n;i++)
{
if(ans+a[i]<=x) ans+=a[i];
else ans=a[i],num++;
}
return num>=m;//如果段数多于题目要求,就在区间右边继续找
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)//答案必在 max(a[i])与sum(a[i])直接,二分找答案
{
cin>>a[i];
l=max(l,a[i]);
r+=a[i];
}
while(l<=r)
{
mid=(l+r)>>1;
if(checkk(mid))l=mid+1;
else r=mid-1;
}
cout<
丢瓶盖
地上丢了A个瓶盖,为了简化问题,我们可以当作这A个瓶盖丢在一条直线上,现在他想从这些瓶盖里找出B个,使得距离最近的2个距离最大,他想知道,最大可以到多少呢?
分析:最小值最大化问题->二分求答案:二分区间l=0,r=最远的两个瓶盖距离
最近的两个瓶盖距离为d,则满足该取法的 其他任意两个瓶盖距离>=A[pos]+d //pos为上一个满足条件的位置。我们最左端的瓶盖开始取。
#include
using namespace std;
const int maxn=100005;
int n,m,a[maxn];
int l,r,mid;
inline bool checkk(int x)//间距为x
{
int pos=a[1]+x,num=1;
for(int i=2;i<=n;i++)
{
if(a[i]>=pos)
{
pos=a[i]+x;
num++;
}
}
return num>=m;//如果可取瓶盖数多于题目要求,就在区间右边继续找
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
l=0,r=a[n]-a[1];
int ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(checkk(mid))l=mid+1,ans=max(ans,mid);
else r=mid-1;
}
cout<