P8646 [蓝桥杯 2017 省 AB] 包子凑数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:有n种含不同数量包子的蒸笼,每种蒸笼的数量是无限的,问有多少种包子数是凑不出来的
思路:1.由裴蜀定理可知,只有当最大公约数为1时,才会有有限个凑不出来的数,也就是说只要gcd > 1, 就直接输出inf即可
2.剩下的考虑用dp来凑数
dp思路:如果某数i可以凑出,那么dp[i]就置为1,那么显然dp[i + a[j]]就都可以被凑出,每一轮全部标记为1就可以了
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 110;
int a[N];
void solve()
{
int n;
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
int g = 0;
for(int i = 1; i <= n; i ++ )
g = __gcd(g, a[i]);
if(g > 1)
{
cout << "INF" << endl;
return;
}
bool dp[100100] = {0};
dp[0] = 1;
for(int i = 0; i <= 10000; i ++ ) //dp凑数
{
if(dp[i])
for(int j = 1; j <= n; j ++ )
dp[i + a[j]] = 1;
}
int ans = 0;
for(int i = 1; i <= 10000; i ++ )
if(!dp[i]) ans ++;
cout << ans << endl;
}
signed main()
{
int t = 1;
//cin >> t;
while(t -- ) solve();
return 0;
}
P8787 [蓝桥杯 2022 省 B] 砍竹子 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:1.通过观察(bushi,也可以先打个表看看最多几次就能砍成1)可以发现每棵树变成1的速度非常快,即每棵树其实不会被砍几次就变成1了,那我们就可以考虑这一堆树,以每棵树需要砍几次为依据对树进行分类(分层)。
2.然后直接枚举每一层去模拟即可(同一高度的树统一砍一次,降到下一层),具体看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 2e6 + 10;
int h[N];
int f[N];
int cnt[N];
int getv(int x)
{
return sqrtl(x / 2 + 1);
}
void solve()
{
int n;
cin >> n;
for(int i = 1; i <= n; i ++ ) cin >> h[i];
int maxx = 0;
for(int i = 1; i <= n; i ++ )
{
int tmp = h[i];
while(tmp > 1)
{
tmp = getv(tmp);
cnt[i] ++;
}
maxx = max(maxx, cnt[i]);
}
int ans = 0;
for(int i = maxx; i >= 1; i -- )
for(int j = 1; j <= n; j ++ ) //每有一个跟前面的不相同的ans就++
if(cnt[j] == i) //如果这颗树是当前层的话(第j棵树是第i层的话)
{
if(h[j] != h[j + 1]) ans ++;
cnt[j] --, h[j] = getv(h[j]); //放在括号里面啊!!
}
cout << ans << endl;
}
signed main()
{
int t = 1;
//cin >> t;
while(t -- ) solve();
return 0;
}
P8655 [蓝桥杯 2017 国 B] 发现环 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:这是一颗基环树,找出树上的环
思路:建双向图,用拓扑排序的思想从外部到内部消除边的影响,入度为1的点显然不会对环造成贡献,故只需将入度为1的点入队,去删除它所连的边即可;将拓扑后的度为1的值标记上,则剩下的未标记的点就是在环上的节点
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n;
vector<int> g[N];
int d[N];
bool st[N];
void topsort()
{
//入度为1的点对环无贡献,故直接通过拓扑的思想删掉连边即可
queue<int> q;
for(int i = 1; i <= n; i ++ )
if(d[i] == 1)
{
q.push(i);
st[i] = 1;
}
while(q.size())
{
int u = q.front();
q.pop();
for(int i = 0; i < g[u].size(); i ++ )
{
int nx = g[u][i];
if(-- d[nx] == 1)
{
q.push(nx);
st[nx] = 1;
}
}
}
}
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++ )
{
int u, v;
cin >> u >> v;
g[u].push_back(v); //建立了无向图之后每个点的入度都至少为1
g[v].push_back(u);
d[u] ++;
d[v] ++;
}
topsort();
for(int i = 1; i <= n; i ++ )
if(!st[i]) cout << i << " ";
}
signed main()
{
int t = 1;
while(t -- ) solve();
return 0;
}
题意:就是找出长度大于等于K的,以c1为首,c2为尾的子串有多少个
思路:双指针,保证两个指针间距为K,算一个有关c1的前缀和或者是c2的后缀和,以c2的后缀和为例,从后往前统计c2的数量,若是碰到c1,则显然ans += sum(c2)
两个指针,i找c2,j找c1
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 10;
void solve()
{
int k;
string s;
char a, b;
cin >> k >> s >> a >> b;
s = "$" + s;
int cnt = 0;
int ans = 0;
//边找a边统计后缀,显然可以想到后缀和
//要有转换的思想在里面
for(int i = s.size() - 1, j = s.size() - k; i >= 1 && j >= 1; i --, j -- )
{
if(s[i] == b) cnt ++;
if(s[j] == a) ans += cnt;
}
cout << ans << endl;
}
signed main()
{
int t = 1;
while(t -- )
{
solve();
}
return 0;
}
P9242 [蓝桥杯 2023 省 B] 接龙数列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:1.可以想到这个dp很像最长上升子序列,o(n ^ 2)可骗50分。用a[i]存该数的头部,b[i]存该数的尾部,则if(a[i] == b[j]) dp[i] = max(dp[i], dp[j] + 1);
2.考虑优化,在最长上升子序列dp转移的条件是 if (a[i] > a[j]),有一个大于的限制关系,而在本题中满足转移的条件是转移关系,可以考虑在顺次dp的过程中,开一个辅助数组,暂存以当前数为结尾的子序列的最长是多少,后面的直接接就可以了。
50分:
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n;
int a[N], b[N];
int dp[N];
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++ )
{
string s;
cin >> s;
a[i] = s[0] - '0', b[i] = s[s.size() - 1] - '0';
}
b[0] = INT_MAX;
for(int i = 1; i <= n; i ++ )
{
dp[i] = 1;
for(int j = 0; j <= i - 1; j ++ ) //可考虑优化掉一维
{
if(b[j] == a[i])
dp[i] = max(dp[i], dp[j] + 1);
}
}
cout << n - *max_element(dp + 1, dp + n + 1) << endl;
}
signed main()
{
int T = 1;
while(T -- ) solve();
return 0;
}
100分:
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n;
int a[N], b[N];
int dp[N], g[N];
//g数组存的是以数字i为结尾的接龙序列的最大长度
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++ )
{
string s;
cin >> s;
a[i] = s[0] - '0', b[i] = s[s.size() - 1] - '0';
}
b[0] = INT_MAX;
for(int i = 1; i <= n; i ++ )
{
dp[i] = 1;
dp[i] = g[a[i]] + 1; //g[a[i]]即当前子序列可以往上接的
g[b[i]] = max(g[b[i]], dp[i]); //更新以当前数的尾巴为结尾的子序列的最大长度
}
cout << n - *max_element(dp + 1, dp + n + 1) << endl;
}
signed main()
{
int T = 1;
while(T -- ) solve();
return 0;
}