👉 一 扩散——广度优先搜索
注意坐标可能是负数就可以了,因此,我们把初始点整体右移一定距离,然后常规BFS。
#include<bits/stdc++.h>
using namespace std;
string s11,s22 ,s1 ,s2;
int N = 3000;
int a[8000][8000];
int times[8000][8000];
typedef pair<int,int> P;
P beg,cur,next,end;
queue<P> Q;
int dir[4][2] = { {1,0},{0,1},{-1,0},{0,-1}};
int main(){
a[N][N] = 1;
a[2020+N][11+N] = 1;
a[11+N][14+N] = 1;
a[2000+N][2000+N] = 1;
beg.first = N;beg.second = N;
Q.push(beg);
beg.first = 2020+N;beg.second = 11+N;
Q.push(beg);
beg.first = 11+N;beg.second = 14+N;
Q.push(beg);
beg.first = 2000+N;beg.second = 2000+N;
Q.push(beg);
int tx, ty;
while(!Q.empty()){
cur= Q.front();Q.pop();
if(times[cur.first][cur.second] == 2020){
break;
}
for(int i = 0 ; i < 4; ++i){
tx = cur.first + dir[i][0];
ty = cur.second + dir[i][1];
if(a[tx][ty] == 1){
continue;
}
else{
times[tx][ty] = times[cur.first][cur.second] + 1;
a[tx][ty] = 1;
next.first = tx; next.second = ty;
Q.push(next);
}
}
}
int ans = 0;
for(int i = 0 ; i < 8000; ++i){
for(int j = 0 ; j < 8000; ++j){
if(a[i][j]){
ans++;
}
}
}
cout<<ans; //20312088
return 0;
}
👉 二 阶乘约数——数论
题目链接
思路
根据数论知识:任意一个正整数都可以写成若干质数的乘积,X = p1^α1 * p2^ α2 …… * pk^αk
那么约数的个数p = (α1+1)*(α2 +1)*…*(αk +1)
有了上述基础,那么n!的约数个数也就是先把n!转为若干质数的乘积,并统计每一个质数的个数,然后根据公式求得约数个数即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
map<int , int > m;
signed main(){
//统计n个数的若干质数乘积。
for(int i = 2; i <=100;++i){
int t = i;
//统计每一个数t的若干质数乘积
for(int j = 2 ; j <= t ;){
if(t % j == 0){
t = t / j;
m[j] ++;
}else{
j ++;
}
}
}
map<int,int>::iterator it = m. begin();
//根据公式求得约数。
int ans = 1;
for(;it!=m.end();++it){
ans = ans*(it->second+1);
}
cout<<ans;
return 0;
}
👉 三 本质上升序列——动态规划
题目链接
思路
据说90%的字符串问题都可以用 动态规划(dp) 解决哦
动态规划三大步, 1.设状态表示方程 2.找状态转移方程 3.状态方程初始化
dp 思路:题目要求去重递增子序列的个数,不妨拆开来做,即先求递增子序列的个数,再考虑去重。
1.假设dp[i]表示前i个的递增子序列的个数(通常为题目所求结果即为状态表示方程)
2. 由于是递增子序列,那么 需满足前提 dp[i] > dp[j],有 dp[i] = dp[i] + dp[j]。 显然j < i。
3.因为每一个str[i]是一个递增子序列,那么初始化 dp[i] = 1;
现在考虑去重,在第2个中的状态转移方程中,我们只考虑了dp[i] > dp[j]的情形,那么如果dp[i] == dp[j]和dp[i]<dp[j]的情形呢,
- 对于dp[i] < dp[j],不符合题意,continue掉;
- 对于dp[i] == dp[j],说明此时有两个字符是相同的,那么dp[j]与dp[j]前面的子序列组成的情况 和 dp[i]与dp[j]前面子序列组成的情况必然会出现重复统计,例如 a n n,dp[0] = 1 dp[1] = 3 dp[2] = 3 ,这里重复统计了一次an的递增子序列,即a、 n、 an ,所以有dp[i] = dp[i] - dp[j]。
因此,总的状态转移为:
if(str[i] > str[j]){
dp[i] = dp[i] + dp[j];
} else if(str[i] == str[j]){
dp[i] = dp[i] - dp[j];
}
#include<bits/stdc++.h>
using namespace std;
#define int long long
set<string> sets;
string str;
string s;
// 共有2的200次方种方案, 铁定超时,所以下面用dp做。
void dfs(int i){
if(i >= str.size()){
sets.insert(s);
return;
}
if(str[i] > s[s.size()-1]){ //可选
s+=str[i];
dfs(i+1);
s.erase(s.size()-1);
}
dfs(i+1); //不选
}
int dp[1000];
signed main(){
str = "tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl";
// dfs(0);
// cout<<sets.size() - 1;
dp[0] = 1;
for(int i = 1 ; i < str.size() ; ++i){
dp[i] = 1; //每个本身有一个。
for(int j = 0; j < i; ++j){
if(str[i] > str[j]){
dp[i] = dp[i] + dp[j];
} else if(str[i] == str[j]){
dp[i] = dp[i] - dp[j]; //去重 假如有 a n n ,那么dp[1] = 3, dp[2] = 3 因此是重复统计了一次an的子序列,即a n an
}
}
}
int ans = 0;
for(int i = 0 ;i < str.size();++i) ans += dp[i];
cout<<ans;
return 0;
}
👉 四 玩具蛇——DFS
题目链接
确定了一个位置后,下一个位置可能的情况就知道了,这就可以用深度优先搜索来做。题目的意思就是要求1-16依次连续放入方格中的方案数量。
#include<bits/stdc++.h>
using namespace std;
int dir[4][2] = { {0,1}, {1,0},{-1,0},{0,-1}};
int vis[10][10];
int cnt;
int k;
void dfs(int x, int y){
if(k == 16){
cnt ++;
// for(int i = 1; i <= 4; ++i){
// for(int j = 1; j <= 4; ++j){
// cout<<vis[i][j]<<" ";
// }
// cout<<'\n';
// }
// cout<<'\n';
return;
}
for(int i = 0 ; i < 4; ++i){
int tx = x + dir[i][0];
int ty = y+dir[i][1];
if(tx>=1 &&tx <= 4 && ty >= 1 && ty <= 4 && vis[tx][ty] == 0){
k++;
vis[tx][ty] = k;
dfs(tx, ty);
k--;
vis[tx][ty] = 0;
}
}
}
int main(){
for(int i = 1; i <= 4; ++i){
for(int j = 1; j <= 4; ++j){
k = 1;
vis[i][j] = 1;
dfs(i,j);
vis[i][j] = 0;
}
}
cout<<cnt;
return 0;
}