欢迎交流,有什么问题,还请指出!!!
目录
P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题
P1002 [NOIP2002 普及组] 过河卒
原题链接 :
思路 :
一道很典型的线性dp问题,应该可以归类于三角形dp问题。
每个点能由其上方的点走下来,也能由左边的点走过来,故到点S(i,j)的方案数等于S(i-1,j)+S(i,j-1),所以动态转移方程 : dp[i][j] = dp[i-1][j] + dp[i][j-1];
本题要注意的是 :
- 一定要加long long(十年oi一场空,不开long long 见祖宗!!!),不然可能就只过1,2,5,然后还找不到代码哪里有问题,hhh;
- 马所在的点(x,y)和马能一步走到的点(p,r)((p-x)^2+(r-y)^2==5)都是一个陷阱,不能经过,也就是到该点路线数为0;
然后请看代码 :
代码 :
A (自己写的,aaa):
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long LL;
int gcd(int a,int b){ return b==0 ? a : gcd(b,a%b); }
int lcm(int a,int b){ if(a==0||b==0) return 0; return (a*b)/gcd(a,b); }
bool is_prime(int x){if(x<2) return false;
for(int i=2;i<=x/i;i++) if(x%i==0) return false; return true;}
const int N = 1e5+10;
int n,m,x,y;
LL a[25][25] = {0},dp[25][25] = {0};
inline void solve(){
cin>>n>>m>>x>>y;
n++;m++;x++;y++;
a[x][y] = 1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if( (pow(i-x,2)+pow(j-y,2)) == 5)
a[i][j] = 1;
dp[1][1] = 0;
for(int i=2;i<=n;i++){
if(a[i][1]==1) break;
else dp[i][1] = 1;
}
for(int j=2;j<=m;j++){
if(a[1][j] == 1) break;
else dp[1][j] = 1;
}
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
if(a[i][j] == 1) dp[i][j] = 0;
else dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
// 测试代码
// for(int i=1;i<=n;i++){
// for(int j=1;j<=m;j++){
// cout << dp[i][j] << " ";
// }
// cout << endl;
// }
cout << dp[n][m];
}
int main()
{
IOS
int _;
// cin >> _;
_ = 1;
while(_ --) solve();
return 0;
}
B(EK的代码) :
这里用到了一个bitset来记录马点以及马一步能到的点的状态为true,较代码A,能节省不少空间。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long LL;
int gcd(int a,int b){ return b==0 ? a : gcd(b,a%b); }
int lcm(int a,int b){ if(a==0||b==0) return 0; return (a*b)/gcd(a,b); }
bool is_prime(int x){if(x<2) return false;
for(int i=2;i<=x/i;i++) if(x%i==0) return false; return true;}
const int N = 30;
int n,m,x,y;
LL dp[25][25] = {0};
bitset<N> mp[N];
int dx[8] = {1,1,-1,-1,2,2,-2,-2};
int dy[8] = {2,-2,2,-2,1,-1,1,-1};
inline void solve(){
cin>>n>>m>>x>>y;
n++;m++;x++;y++;
for(int i=0;i<8;i++){
int nx = x + dx[i],ny = y + dy[i];
if(1<=nx&&nx<=n&&1<=ny&&ny<=m) mp[nx][ny] = true;
}
mp[x][y] = true;
dp[1][1] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1&&j==1) continue;
dp[i][j] = dp[i-1][j] + dp[i][j-1];
if(mp[i][j]) dp[i][j] = 0;
}
}
cout << dp[n][m] << endl;
return ;
}
int main()
{
IOS
int _;
// cin >> _;
_ = 1;
while(_ --) solve();
return 0;
}
例题推荐 :
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
P1003 [NOIP2011 提高组] 铺地毯
原题链接 :
[NOIP2011 提高组] 铺地毯 - 洛谷
思路 :
使用结构体存储每个地毯的a,b,g,k的信息,因为从小到大,所以从后往前遍历,找到第一个地毯(x,y)在其范围内的输出其编号即可!!!
代码 :
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
struct node{
int a,b,g,k;
}d[N];
int n,x,y;
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>d[i].a>>d[i].b>>d[i].g>>d[i].k;
cin>>x>>y;
int ans = 0;
for(int i=n;i>=1;--i){
if(x>=d[i].a&&x<=d[i].a+d[i].g && y>=d[i].b&&y<=d[i].b+d[i].k){
ans = i;
break;
}
}
cout << ans << endl;
}
P1008 [NOIP1998 普及组] 三连击
原题链接 :
https://www.luogu.com.cn/problem/P1008
思路 :
直接暴力枚举即可,值得注意的是Ek是直接枚举的三位数,也就是(100,1000),但是仔细想一下,真正的范围应该在(123,333);
代码 :
(很久以前写的c代码,hh)
#include <stdio.h>
int main()
{
int a,b,c;
for(a=123;a<=333;a++)
{
b=a*2;
c=a*3;
if((a/100+a/10%10+a%10+b/100+b/10%10+b%10+c/100+c/10%10+c%10==1+2+3+4+5+6+7+8+9)&&((a/100)*(a/10%10)*(a%10)*(b/100)*(b/10%10)*(b%10)*(c/100)*(c/10%10)*(c%10)==(1)*(2)*(3)*(4)*(5)*(6)*(7)*(8)*(9)))
printf("%d %d %d\n",a,b,c);
}
return 0;
}
P1028 [NOIP2001 普及组] 数的计算
原题链接 :
思路 :
后缀和 + 动态规划
设状态dp[i][j]表示长度为i且最后一位数值为j的方法数量,容易得到状态转移方程:
dp[i][j] = Sum(dp[i-1][k])<k=2j,n>
右边这个式子是可以通过后缀和处理出来的。
最后的答案就是将整个dp数组求和
代码 :
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long LL;
int gcd(int a,int b){ return b==0 ? a : gcd(b,a%b); }
int lcm(int a,int b){ if(a==0||b==0) return 0; return (a*b)/gcd(a,b); }
bool is_prime(int x){if(x<2) return false;
for(int i=2;i<=x/i;i++) if(x%i==0) return false; return true;}
const int N = 1e3+10;
LL dp[N][N],sf[N],ans;
int n;
inline void solve(){
cin>>n;
dp[1][n] = 1;
for(int i=1;i<=n;i++) sf[i] = 1;
for(int i=2;i<=n;i++){
for(int j=1;j<=n/2;j++){
dp[i][j] = sf[2*j];
}
for(int j=n;j>=1;j--) sf[j] = sf[j+1] + dp[i][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans += dp[i][j];
cout << ans << '\n';
return ;
}
int main()
{
IOS
int _;
// cin >> _;
_ = 1;
while(_ --) solve();
return 0;
}
P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题
原题链接 :
[NOIP2001 普及组] 最大公约数和最小公倍数问题 - 洛谷
思路 :
枚举 + gcd() + lcm(),这题主要考察的是你怎么求gcd()和lcm(),也就是最小公倍数,和最大公约数,
我的求法 :
int gcd(int a,int b){ return b==0 ? a : gcd(b,a%b); }
int lcm(int a,int b){ if(a==0||b==0) return 0; return (a*b)/gcd(a,b); }
Ek的求法 :
LL gcd(LL a,LL b){ return b==0 ? a : gcd(b,a%b); }
LL lcm(LL a,LL b){ return a / gcd(a,b) * b ; }
本质一样,原理相同!!!
这题可以利用lcm和gcd的性质将二维的时间复杂度优化到一维,
gcd(a,b) = x && lcm(a,b)=y,那么x*y/a = b;!!!
代码 :
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b){ return b==0 ? a : gcd(b,a%b); }
LL lcm(LL a,LL b){ return a / gcd(a,b) * b ; }
bool is_prime(int x){if(x<2) return false;
for(int i=2;i<=x/i;i++) if(x%i==0) return false; return true;}
const int N = 2e5+10;
int x,y;
LL ans;
inline void solve(){
cin>>x>>y;
for(int i=x;i<=y;i++){
int j = x*y/i;
if(gcd(i,j)==x&&lcm(i,j)==y) ans ++;
}
cout<<ans<<endl;
}
int main()
{
IOS
int _;
// cin >> _;
_ = 1;
while(_ --) solve();
return 0;
}