慢慢写,尽量全部写完
目录
- 1001 方块与收纳盒
- 1002 舔狗舔到最后一无所有
- 1003 可爱の星空
- 1004 数字三角形
- 1005 花店橱窗
- 1006 免费馅饼
- 1007 钉子和小球
- 1008 [NOIP2002]过河卒
- 1009 [NOIP2008]传球游戏
- 1010 「木」迷雾森林
- 1011 [NOIP2004]合唱队形
- 1012 [NOIP1999]拦截导弹
- 1013 数学考试
- 1014 小A买彩票
- 1015 购物
- 1016 牛牛的旅游纪念品
- 1017 [NOIP2001]装箱问题
- 1018 [NOIP2005]采药
- 1019 [NOIP2006]开心的金明
1001 方块与收纳盒
简单斐波那契模型
#include <bits/stdc++.h>
#define int long long
using namespace std;
int f[100];
signed main(){
int t;scanf("%lld",&t);
f[0] = f[1] = 1;
for (int i=2;i<=80;i++) f[i] = f[i-1] + f[i-2];
while (t--){
int n;scanf("%lld",&n);
printf("%lld\n",f[n]);
}
}
1002 舔狗舔到最后一无所有
转移方程很简单,个人觉得有点难想。
定义f[i][j]为第i天去第j家店的方案数。对于第i天而已,如果选择去了第0家店,那么对于前i-1天和前i-2天,就只能去第1家店或第2家店,即能够到f[i][0]的路径数为到达f[i-1][1]、f[i-1][2]、f[i-2][1]、f[i-2][2]的路径数总和,于是可以写出转移方程:
f[i][0] = (f[i-1][1] + f[i-1][2] + f[i-2][1] + f[i-2][2]) % mod;
f[i][1] = (f[i-1][0] + f[i-1][2] + f[i-2][0] + f[i-2][2]) % mod;
f[i][2] = (f[i-1][1] + f[i-1][0] + f[i-2][1] + f[i-2][0]) % mod;
同时发现,对于每一天的总方案数:
f[i][0]+f[i][1]+f[i][2] = (f[i-1][0]+f[i-1][1]+f[i-1][2])*2%mod + (f[i-2][0]+f[i-2][1]+f[i-2][2])*2%mod;
故我们可以直接优化成一维数组。
//未优化代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 7;
const int mod = 1e9 + 7;
int f[N][3];
signed main(){
int t;scanf(" %lld",&t);
f[1][0] = 1,f[1][1] = 1,f[1][2] = 1;
f[2][0] = 3,f[2][1] = 3,f[2][2] = 3;
for (int i=3;i<=1e5;i++){
f[i][0] = (f[i-1][1] + f[i-1][2] + f[i-2][1] + f[i-2][2]) % mod;
f[i][1] = (f[i-1][0] + f[i-1][2] + f[i-2][0] + f[i-2][2]) % mod;
f[i][2] = (f[i-1][1] + f[i-1][0] + f[i-2][1] + f[i-2][0]) % mod;
}
while (t--){
int n;scanf(" %lld",&n);
printf("%lld\n",(f[n][0]+f[n][1]+f[n][2])%mod);
}
}
//优化代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 7;
const int mod = 1e9 + 7;
int dp[N];
signed main(){
int t;scanf(" %lld",&t);
dp[1] = 3,dp[2] = 9;
for (int i=3;i<=1e5;i++){
dp[i] = (dp[i-1]*2%mod + dp[i-2]*2%mod) % mod;
}
while (t--){
int n;scanf(" %lld",&n);
printf("%lld\n",dp[n]);
}
}
1003 可爱の星空
很有趣的题目。
可以设i个点的贡献为f[i],如果i是偶数,那么i连通块可以分成两个大小相等的偶数连通块,有f[i] = f[i/2] * 2;如果i是奇数,那么i连通块可分为一奇一偶大小相差为1的连通块,有f[i] = f[i/2] + f[i/2+1] + 1。 因为i特别大,所以我采用了记忆化搜索。
#include <bits/stdc++.h>
#define int long long
using namespace std;
map<int,int>mp;
int deal(int x){
if (x==0||x==1) return 0ll;
if (mp[x]) return mp[x];
if (x%2ll) mp[x] = deal(x/2ll) + deal(x/2ll+1ll) + 1ll;
else mp[x] = 2ll * deal(x/2ll);
return mp[x];
}
signed main(){
int t;
while (scanf("%lld",&t)!=EOF){
while (t--){
int n;scanf("%lld",&n);
printf("%lld\n",deal(n));
}
}
}
1004 数字三角形
规律题。
#include <bits/stdc++.h>
using namespace std;
signed main(){
int n;scanf(" %d",&n);
int cnt = 1;
for (int i=1;i<=n;i++){
for (int j=1;j<=i;j++){
printf("%4d",cnt);
cnt++;
}
printf("\n");
}
}
1005 花店橱窗
写了很久的题目。
所有的花束在放入花瓶时必须保持其标识数的顺序是本题解题点。
定义dp[i][j]为选了第i行j列的花能获得的最大值。暴力枚举上一行的最大值,全部枚举找到最大值累加即为答案。
具体看代码。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e9 + 7;
int n,m;
int a[110][110],dp[110][110];
int vis[110][110];
void print(int x,int y){//输出路径
if (y==1){
return;
}
print(vis[y][x],y-1);
printf("%d ",vis[y][x]);
}
signed main(){
scanf(" %d%d",&n,&m);
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
scanf(" %d",&a[i][j]);
dp[i][j] = a[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=i;j<=m;j++){//为什么从第i列开始?因为第i-1列已经确定
int t = 0;
int maxn = -MAXN;
for (int k=j-1;k>=i-1;k--){//寻找i-1行的最大值 同样注意遍历范围
if (maxn<=dp[i-1][k]){
t = k;
maxn = dp[i-1][k];
}
}
if (maxn!=-MAXN) dp[i][j] += maxn,vis[i][j] = t;//记录路径
}
}
int pos = 0,ans = -MAXN;
/*for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
cout<<dp[i][j]<<" ";
}
cout<<endl;
}*/
for (int i=n;i<=m;i++){
if (ans<dp[n][i]){
ans = dp[n][i];
pos = i;
}
}
printf("%d\n",ans);
print(pos,n);
printf("%d\n",pos);
}
1006 免费馅饼
很好的题目,可惜题面有锅。
用a[i][j]记录j位置i秒到达最底下的馅饼的分数,f[i][j]定义为j坐标前i秒带来的最大贡献。
发现f[i][j]只有可能从f[i-1][j]、f[i-1][j-1]、f[i-1][j-2]、f[i-1][j+1]、f[i-1][j+2]转移过来。
输出要按从左到右,题目没有说明,导致我WA了n发。。
#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
int f[1100][110],a[1100][110];
int p[1100][110];
int dy[] = {-2,-1,0,1,2};//从左到右 注意这个顺序不能变!
int w,h,n,ans;
int t,x,v,wi,st;
void print(int xi,int yi){//路径输出
if (xi==0) return;
print(xi-1,yi+p[xi][yi]);
if (xi==1) p[xi][yi] = st - yi;
printf("%lld\n",-p[xi][yi]);
}
signed main(){
scanf(" %lld%lld",&w,&h);
while (scanf(" %lld%lld%lld%lld",&t,&x,&v,&wi)!=EOF){
if((h-1)%v)continue;
int ti = t + (h-1)/v;
a[ti][x] += wi;
n = max(ti,n);
}
int ans_x,ans_y;
st = w / 2 + 1;//初始位置正中间
memset(f,-INF,sizeof(f));
f[0][w/2+1]=0;
for (int i=1;i<=n;i++){
for (int j=1;j<=w;j++){
for (int k=0;k<5;k++){//枚举路径
int pos=j+dy[k];
if(pos>=1&&pos<=w){
int qi = f[i-1][pos] + a[i][j];
if(f[i][j]<qi){
f[i][j]=qi;
p[i][j]=dy[k];//记录路径
}
}
}
}
}
for(int i=1;i<=w;i++){
if(f[n][i]>ans){
ans=f[n][i];
ans_y=i;
}
}
cout<<ans<<'\n';
print(n,ans_y);
}
1007 钉子和小球
待补
1008 [NOIP2002]过河卒
f[i][j]为到达i行j列的总方案,mp[i][j]记录这个点是否走过。
无脑地把马能到的和它自身的点的mp置1即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,y;
int f[110][110],mp[110][110];
int dx[] = {-2,-2,-1,-1,1,1,2,2,-1,0};
int dy[] = {-1,1,-2,2,-2,2,-1,1,0,-1};
bool check(int x,int y){
return x < 0 || x > n || y < 0 || y > m || mp[x][y];
}
int dfs(int xi,int yi){
if (f[xi][yi]) return f[xi][yi];
//cout<<xi<<" "<<yi<<endl;
for (int i=8;i<=9;i++){//枚举卒的路径
int xx = xi + dx[i],yy = yi + dy[i];
if (check(xx,yy)) continue;
f[xi][yi] += dfs(xx,yy);
}
return f[xi][yi];
}
signed main(){
scanf(" %lld%lld%lld%lld",&n,&m,&x,&y);
mp[x][y] = 1;
for (int i=0;i<8;i++){//枚举马能到的八个方向
int xx = x + dx[i],yy = y + dy[i];
mp[xx][yy] = 1;
}
f[0][0] = 1;
cout<<dfs(n,m);
}