A.模拟
很明显,对于所有的非000的位,我们最好的处理方式为将其移动到个位,然后实行减一操作
#include<bits/stdc++.h>
using namespace std;
char s[110];
int main()
{
int t;scanf("%d",&t);
while (t--)
{
int n;scanf("%d",&n);
scanf("%s",s+1);
int ans = 0;
for (int i=1;i<=n;++i)if (s[i]!='0')
{
++ans;
ans+=(int)(s[i]-'0');
}
if (ans>0&&s[n]!='0')--ans;
printf("%d\n",ans);
}
}
B.思维+二分+双指针
考虑到a,ba,ba,b数组中的没有相同的数
那么aaa数组要是想要在字典序上小于bbb数组
aaa数组的第一位一定要小于bbb数组的第111位
我们可以枚举所有的bbb数组的第一位的可能,计算aaa数组第一位小于bbb数组第一位的最小代价
即,如果我们选择b[i]b[i]b[i]为bbb数组第一位,这个决定本身需要代价i−1i-1i−1
然后我们找到最小的jjj使得a[j]<b[i]a[j]<b[i]a[j]<b[i]
因此总代价为i−1+j−1i-1+j-1i−1+j−1
找jjj的操作,我们可以先得一个新数组c[i]=min(a[j],j≤i)c[i]=min(a[j],j\le i)c[i]=min(a[j],j≤i)
在,ccc中二分即可
当然我们还有双指针的线性解法
同样我们先得到ccc数组,然后枚举bbb数组
但是在枚举bbb数组时,我们可以注意到bbb数组的开销是一直增大的
倘若我们想得到更小的答案,那么aaa数组的jjj一定要减小的
换而言之,我们这次取的b[i]b[i]b[i]一定比上次小
而jjj也只可能减小不可能增大
因此,双指针复杂度O(n)O(n)O(n)
//二分
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
int a[maxn],b[maxn];
int n;
inline int solve(int x)
{
int l = 1,r = n;
int ans = 2*n;
while (l<=r)
{
int mid = l+r>>1;
if (a[mid]<x)
{
ans = mid;
r = mid-1;
}
else
{
l = mid+1;
}
}return ans-1;
}
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
for (int i=1;i<=n;++i)scanf("%d",&a[i]);
for (int i=1;i<=n;++i)scanf("%d",&b[i]);
for (int i=2;i<=n;++i)a[i]=min(a[i],a[i-1]);
int ans = 2*n;
for (int i=1;i<=n;++i)
ans = min(ans,solve(b[i])+i-1);
printf("%d\n",ans);
}
}
//双指针
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
int a[maxn],b[maxn];
int n;
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
for (int i=1;i<=n;++i)scanf("%d",&a[i]);
for (int i=2;i<=n;++i)a[i]=min(a[i-1],a[i]);
for (int i=1;i<=n;++i)scanf("%d",&b[i]);
int p1 = n,p2 = 1;
int ans = 2*n;
for (int i=1;i<=n;++i)if (b[i]>=b[p2])
{
p2 = i;
while (p1>0&&a[p1]<b[p2])--p1;
++p1;
ans = min(ans,p1+p2-2);
}
printf("%d\n",ans);
}
}
C.bfsbfsbfs
类似于bfsbfsbfs,是搜索的一种变种
每轮搜索我们从111遍历到nnn
去掉每一个度数为000的节点,同时更新出新的度数为000的节点
我们直到,当我们遍历到iii,她更新出了vvv,使得vvv的入度为000
- v>iv>iv>i本轮下vvv可以被遍历到
- v<iv<iv<i vvv只能留着下轮便利了
因此,我们不妨用setsetset维护本轮遍历的所有的度数为000的节点
对于v>iv>iv>i,我们将vvv放入本轮的setsetset
对于v<iv<iv<i我们新开一个setsetset作为下轮的放入
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
vector<int> G[maxn];
int du[maxn];
int n;
int main()
{
int t;scanf("%d",&t);
while (t--)
{
set<int> se;
int n;scanf("%d",&n);
for (int i=0;i<=n;++i)G[i].clear();
for (int i=1;i<=n;++i)
{
scanf("%d",&du[i]);
for (int j=1;j<=du[i];++j)
{
int u;scanf("%d",&u);
G[u].push_back(i);
}
if (du[i]==0)se.insert(i);
}
int ans=0;
set<int> tmp;
while (!se.empty())
{
int u = *se.begin();
se.erase(se.begin());
for (int v:G[u])
{
--du[v];
if (du[v]==0)
{
if (v<u)tmp.insert(v);
else se.insert(v);
}
}
if (se.empty())
{
++ans;
swap(se,tmp);
}
}
for (int i=1;i<=n;++i)if (du[i]>0)
{
ans=-1;
break;
}
printf("%d\n",ans);
}
}
D.构造
a⊕b⊕ca\oplus b\oplus ca⊕b⊕c a,b,c∈{0,1}a,b,c\in\{0,1\}a,b,c∈{0,1}
什么时候为111,什么时候为000
答案是,a+b+ca+b+ca+b+c为奇数的时候为111,偶数的时候为000
因此,我们可以确定。我们消去111的操作,一次操作只可以消去222个
111个数的奇偶性不变,因此如果111的个数为奇数,那么直接不可能
我们选取iii,如果[ai,ai+1,ai+2][a_i,a_{i+1},a_{i+2}][ai,ai+1,ai+2]为[0,1,1],[1,0,1],[1,1,0][0,1,1],[1,0,1],[1,1,0][0,1,1],[1,0,1],[1,1,0]
我们可以消灭111
但是如果为[0,0,1],[1,0,0],[0,1,0][0,0,1],[1,0,0],[0,1,0][0,0,1],[1,0,0],[0,1,0]的话,就不可能了
例如[1,0,0,1,0][1,0,0,1,0][1,0,0,1,0]
我们可以先[1,1,1,1,0][1,1,1,1,0][1,1,1,1,0]然后[1,1,0,0,0][1,1,0,0,0][1,1,0,0,0],再[0,0,0,0,0][0,0,0,0,0][0,0,0,0,0]
主要是,我们需要去和另外一个奇数的111接轨!!
基于这种想法,创造出如下构造方案:
我们遍历i:1→n−2i:1\rightarrow n-2i:1→n−2
如果当前的a[i]==1:a[i]==1:a[i]==1:
记a[i]+a[i+1]+a[i+2]=cnta[i]+a[i+1]+a[i+2]=cnta[i]+a[i+1]+a[i+2]=cnt
1…cnt==2:cnt==2:cnt==2:我们直接进行操作,然后使得i=i+3i=i+3i=i+3
2.cnt==1:cnt==1:cnt==1:我们进行操作,将a[i]=a[i+1]=a[i+2]=1a[i]=a[i+1]=a[i+2]=1a[i]=a[i+1]=a[i+2]=1,然后i=i+2i=i+2i=i+2
3.cnt==3:cnt==3:cnt==3:直接i=i+2i=i+2i=i+2
进行这种操作一周后,剩下来的将是偶数长度的连续的111序列
我们只用222个222个消掉就好了
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
int a[maxn];
int n;
int main()
{
int t;scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
for (int i=1;i<=n;++i)scanf("%d",&a[i]);
vector<int> res;
for (int i=1;i+2<=n;)
{
if (a[i]==1)
{
int cnt = a[i]+a[i+1]+a[i+2];
if (cnt&1)
{
if (cnt!=3)
{
res.push_back(i);
a[i]=a[i+1]=a[i+2]=1;
}
i = i+2;
}
else
{
res.push_back(i);
a[i]=a[i+1]=a[i+2]=0;
i=i+3;
}
}
else ++i;
}
for (int i=1;i+2<=n;++i)if (a[i]+a[i+1]+a[i+2]==2)
{
res.push_back(i);
a[i]=a[i+1]=a[i+2]=0;
}
for (int i=n;i-2>=1;--i)if (a[i]+a[i-1]+a[i-2]==2)
{
res.push_back(i-2);
a[i]=a[i-1]=a[i-2]=0;
}
bool f = true;
for (int i=1;i<=n;++i)if (a[i]==1)
{
f=false;
break;
}
if (!f)
{
printf("NO\n");
continue;
}
printf("YES\n");
printf("%d\n",(int)res.size());
for (int num:res)printf("%d ",num);
puts("");
}
}
E.区间dpdpdp
看这个数据我们大概可以想到这是一个n2n^2n2级别的算法
首先我们说一个结论对一个区间[l,r][l,r][l,r],使得他们的颜色统一
目标颜色为a[l]a[l]a[l]时,所需的操作最少
文字不大好说明,举例试一试就会发现这个规律了
稍稍用到了一些容斥思想
设计dpdpdp状态:dp[i][j]:dp[i][j]:dp[i][j]
为[i,j][i,j][i,j]变为**和a[i]**相同的颜色,可以减少的最大的数
(本来要将[i,j][i,j][i,j]变为相同的颜色的话,需要一个一个的染色,即j−ij-ij−i)
那么最终的答案就是n−1−dp[1][n]n-1-dp[1][n]n−1−dp[1][n]
dpdpdp公式为dp[i][j]=max(dp[i+1][j],max(1+dp[i+1][k−1]+dp[k][j]∣a[i]==a[k]))dp[i][j] = max(dp[i+1][j],max(1+dp[i+1][k-1]+dp[k][j]|a[i]==a[k]))dp[i][j]=max(dp[i+1][j],max(1+dp[i+1][k−1]+dp[k][j]∣a[i]==a[k]))
关于dp[i+1][j]dp[i+1][j]dp[i+1][j]的转移
我们可以认为将[i+1,j][i+1,j][i+1,j]转化为相同的颜色后,再次使用了一次操作将a[i]a[i]a[i]和[i+1,j][i+1,j][i+1,j]的颜色同化
关于max(1+dp[i+1][k−1]+dp[k][j]∣a[i]==a[k])max(1+dp[i+1][k-1]+dp[k][j]|a[i]==a[k])max(1+dp[i+1][k−1]+dp[k][j]∣a[i]==a[k])
我们可以认为先将[i+1,k−1][i+1,k-1][i+1,k−1]颜色归一,再将[k,j][k,j][k,j]归一
因为a[i]a[i]a[i]和a[k]a[k]a[k]颜色相同,所以可以节省一步!
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5010;
int n, a[maxn], la[maxn], fn[maxn];
int f[N][N];
int main() {
int t;scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) fn[i] = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", a + i);
if (a[i] == a[i - 1]) {
--i; --n;
} else {
la[i] = fn[a[i]];
fn[a[i]] = i;
}
}
for (int i = n; i; --i) {
for (int j = i + 1; j <= n; ++j) {
f[i][j] = f[i][j - 1];
for (int k = la[j]; k >= i; k = la[k]) {
f[i][j] = max(f[i][j], f[i][k] + f[k][j - 1] + 1);
}
}
}
printf("%d\n", n - 1 - f[1][n]);
}
return 0;
}
本文解析了五道经典编程题目,包括模拟、思维+二分+双指针、bfs变种、构造和区间dp等算法的应用。通过具体实例详细阐述了解题思路和代码实现。
745

被折叠的 条评论
为什么被折叠?



