P1192
台阶问题,每次走,都是前面n种走法之和,emm你得先走到n种,不然就是你走到此之前的种类,记得初始值是1
#include<iostream>
#include<math.h>
using namespace std;
int f[100002];
int main()
{
int m,n;
cin>>m>>n;
f[0]=1;
for(int j=1;j<=m;j++)
{
for(int i=1;i<=min(j,n);i++)
{
f[j]+=f[j-i];
f[j]%=100003;
}
}
cout<<f[m];
return 0;
}
P1025
这应该是搜索吧,每一次搜索增加一,然后到三的时候,就看和是不是这个数,如果是的话答案就增加1
有一个不得不做的剪枝就是枚举当前划分所用分数时应该从last(上次划分所用分数)枚举到sum+i*(k-cur)<=n为止,因为之后划分的分数一定大于或等于当前划分所用分数。
#include<cstdio>
int n,k,cnt;
void dfs(int last,int sum,int cur)
{
if(cur==k)
{
if(sum==n) cnt++;
return;
}
for(int i=last;sum+i*(k-cur)<=n;i++)//剪枝,只用枚举到sum+i*(k-cur)<=n为止
dfs(i,sum+i,cur+1);
}
int main()
{
scanf("%d%d",&n,&k);
dfs(1,0,0);
printf("%d",cnt);
}
f[i][x]表示i分成x个非空的数的方案数
显然 i<x 时 f[i][x]=0 , i=x 时 f[i][x]=1;
其余的状态,我们分情况讨论:
①有1的 ②没有1的
第一种情况,方案数为 f[i-1][x-1]
第二种情况,方案数为 f[i-x][x] (此时 i 必须大于 x)
所以,状态转移方程为: f[i][x]=f[i-1][x-1]+f[i-x][x],这其实就是背包的思想啊
#include<iostream>
using namespace std;
int f[201][7];
int main()
{
int m,n;
cin>>m>>n;
for(int i=1;i<=m;i++)
{
f[i][1]=1;
f[i][0]=1;
}
for(int i=2;i<=n;i++)
{
f[0][i]=0;
f[1][i]=0;
}
for(int i=2;i<=m;i++)//m个物品
{
for(int j=2;j<=n;j++)//容积是三
{
if(i>j)
f[i][j]=f[i-1][j-1]+f[i-j][j];
else
f[i][j]=f[i-1][j-1];
}
}
cout<<f[m][n];
}
P1057
就想结尾,传到他手里的前提是前面一步是他的左边或者右边
所以初始化是在自己手上,数组应该是传的次数和球所在的位置,
#include<iostream>
using namespace std;
int f[31][31]={1};
int main()
{
int m,n;//m是次数,n 是人数
cin>>n>>m;
for(int i=1;i<=m;i++)
{
for(int j=0;j<n;j++)
{
f[i][j]=f[i-1][(j+1)%n]+f[i-1][(j-1+n)%n];
}
}
cout<<f[m][0];
}
中心思想是判断走了多少步,也就是传了多少次球,然后在这样下面,每个人对应的次数是多少,初始化小蛮自己是1
P1135
奇怪的电梯
两种做法,一种是DP,一种是广搜
首先来看DP,
一个数组里面放按的数量,和当前所在的位置,如何控制顺序,依靠做标记,所以就是dp[到这楼要几次][楼层数]=有没有去过
初始化就是在一楼(说不定目标也在1楼呢,特判要注意,按键数目是1
接下去两重循环,按几次(不好说就往大了定),和每一次上面的的可能
你到过一个楼层,因为需要最小的,所以不可能再次到达,所以就不会再经历,所以只要根据两者之间关系判断是不是B就可以
#include<iostream>
using namespace std;
int a[202],f[202][202];
int main()
{
int N,A,B,iflag=0;
cin>>N>>A>>B;
for(int i=1;i<=N;i++)
{
cin>>a[i];
}
if(A==B)
{
cout<<0;
return 0;
}
f[0][A]=1;
for(int i=0;i<=200;i++)
for(int j=1;j<=N;j++)
{
if(f[i][j])
{
if(j+a[j]<=N)
f[i+1][j+a[j]]=1;
if(j-a[j]>=1)
f[i+1][j-a[j]]=1;
if(f[i+1][B]==1)
{cout<<i+1;
iflag=1;
return 0;
}
}
}
if(iflag==0)
cout<<-1;
return 0;
}
第二种是广搜,应该就是先把a入队,然后通过加减来标志能到的,入队,再一次广搜,直到找到,每一次cnt++
#include<iostream>
#include<queue>
using namespace std;
int N,A,B;
int visited[202],cnt=0,a[202],flag;
queue<int>Q;
queue<int>P;
void bfs(int o,int b)
{
int dx,dy;
while(!Q.empty() )
{
if(dx==B||dy==B)
{ cout<<cnt;
flag=1;
return ;
}
if(Q.front() +P.front() <=N)
{
dx=Q.front() +P.front() ;
if(!visited[dx])
{
visited[dx]=1;
Q.push(dx);
P.push(a[dx]);
cnt++;
}
}
if(Q.front() -P.front() >=1)
{
dy=Q.front() -P.front() ;
if(!visited[dy])
{
visited[dy]=1;
Q.push(dy);
P.push(a[dy]);
cnt++;
}
}
Q.pop() ;
P.pop() ;
}
if(flag==0)
cout<<-1;
}
int main()
{
cin>>N>>A>>B;
if(A==B)
{cout<<0;
return 0;
}
for(int i=1;i<=N;i++)
{
cin>>a[i];
}
visited[A]=1;
Q.push(A);
P.push(a[A]);
bfs(A,a[A]);
}
还可以用floyed,震惊我,确实相当于由边连起来的点,这样子存起来之后,不断进行拉伸
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dis[201][201],n,a,b,x;
int main()
{
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
dis[i][j]=dis[j][i]=1e8;
for(register int i=1;i<=n;i++)
{
scanf("%d",&x);
if(i+x<=n) dis[i][i+x]=1;
if(i-x>=1) dis[i][i-x]=1;
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
if(dis[a][b]<1e8) printf("%d",dis[a][b]);
else printf("-1");
return 0;
}
P1090
他每一次合并之后消耗的体力还会留下来,所以相当于有一部分数字是在一直被利用的,斯怎么有点杨辉三角
应该先排序,然后把最少的合并,合并完以后插入,用优先队列:priority_queue<int,vector,greater >
#include<iostream>
#include<queue>
using namespace std;
int a[10086];
priority_queue<int ,vector<int>,greater<int> >qu;
int main()
{
int m,x,y,ans=0;
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>a[i];
qu.push(a[i]);
}
for(int i=1;i<=m-1;i++)
{
x=qu.top();
qu.pop();
x+=qu.top();
ans+=x;
qu.pop();
qu.push(x);
}
cout<<ans;
}