【思维·观察·细节】删除 纪中集训
Description
Alice上化学课时又分心了,他首先画了一个3行N列的表格,然后把数字1到N填入表格的第一行,保证每个数只出现一次,另外两行他也填入数字1到N,但不限制每个数字的出现次数。
Alice现在想删除若干列使得每一行排完序后完全一样,编程计算最少需要删除多少列。
Input
第一行包含一个整数N(1<=N<=100000),表示表格的列数。
接下来三行每行包含N个整数,每个数在1到N之间,而且第一行的数互不相同。
Output
输出最少需要删除的列数。
Sample Input
输入1:
7
5 4 3 2 1 6 7
5 5 1 1 3 4 7
3 7 1 4 5 6 2
输入2:
9
1 3 5 9 8 6 2 4 7
2 1 5 6 4 9 3 4 7
3 5 1 9 8 6 2 8 7
Sample Output
输出1:
4
输出2:
2
Hint
【样例解释】
例1中Alice需要删除第2、4、6、7这四列,然后每行排完序都是1、3、5。
【数据范围】
40%的数据N<=100
70%的数据N<=10000
考试的时候猜了一个显而易见的结论:
在第二行中没有出现的数出现在了第一行或第三行中的某一列,那么这一列必须要删掉。同理,在第三行中没有出现的数出现在了第一行或第二行中的某一列,那么这一列也必须要删掉。
试了一下,第一组样例过不去,再猜了一个结论:他最终是要同一种数最多只能出现相同的次数,而第一行又是一个排列,所以每个数最多出现一次,那么就把出现次数大于1次的删去。由于时间所剩无几,我码了一下过了样例就交了。
出分的时候原版代码只有20分,把
v
i
s
vis
vis数组去掉压在
c
n
t
cnt
cnt数组(代码中的t1和t2)里面有40分
//20pts
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100005
#define INF 0x3f3f3f3f
#define LL long long
int n,a[MAXN],b[MAXN],c[MAXN],t1[MAXN],t2[MAXN],ans;
bool vis1[MAXN],vis2[MAXN];
int main()
{
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]);
vis1[b[i]]=1;
t1[b[i]]++;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
vis2[c[i]]=1;
t2[c[i]]++;
}
for(int i=1;i<=n;i++)
{
if(vis1[a[i]]==0||vis1[c[i]]==0||vis2[a[i]]==0||vis2[b[i]]==0)
ans++,t1[b[i]]--,t2[c[i]]--;
}
for(int i=1;i<=n;i++)
if(t1[b[i]]>=2||t2[c[i]]>=2)
ans++,t1[b[i]]--,t2[c[i]]--;
printf("%d\n",ans);
return 0;
}
//40pts
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100005
#define INF 0x3f3f3f3f
#define LL long long
int n,a[MAXN],b[MAXN],c[MAXN],t1[MAXN],t2[MAXN],ans;
int main()
{
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]);
t1[b[i]]++;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
t2[c[i]]++;
}
for(int i=1;i<=n;i++)
{
if(t1[a[i]]==0||t1[c[i]]==0||t2[a[i]]==0||t2[b[i]]==0)
ans++,t1[b[i]]--,t2[c[i]]--;
}
for(int i=1;i<=n;i++)
if(t1[b[i]]>=2||t2[c[i]]>=2)
ans++,t1[b[i]]--,t2[c[i]]--;
printf("%d\n",ans);
return 0;
}
而造成这个差异的原因是:有可能在做前面操作的时候对其它做了贡献,因为 t 1 [ ] , t 2 [ ] t1[],t2[] t1[],t2[]都有变化,所以要用 t 1 [ ] , t 2 [ ] t1[],t2[] t1[],t2[]来判断,而不能看最开始的 v i s vis vis数组。
同理,在做其它操作的时候可能会导致 t 1 [ ] , t 2 [ ] t1[],t2[] t1[],t2[]的变化,所以可能出现有新的列满足被删的条件,所以要一直遍历数组,重复做这种操作。做多少次呢?直到它不再更新为止。
我猜的第二个结论也是错的,它不能保障最少,因为第一个结论删去的是必须删的,所以会最少,但是第二个结论中,有很多个那些数的时候删哪些数呢?显然是不能随便删的。(可是我就是随便删了)
还有一个致命的错误在考场上没有注意到,我们需要用一个数组标记当前列是否已经被删,如果已经被删,则不能再次删它qwq 我居然连这个都没有注意到,实在是太菜了
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100005
#define INF 0x3f3f3f3f
#define LL long long
int n,a[MAXN],b[MAXN],c[MAXN],t1[MAXN],t2[MAXN],ans;
bool vis[MAXN];
int main()
{
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]);
t1[b[i]]++;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&c[i]);
t2[c[i]]++;
}
bool f=0;
do
{
f=0;
for(int i=1;i<=n;i++)
{
if(!vis[i]&&(t1[a[i]]==0||t1[c[i]]==0||t2[a[i]]==0||t2[b[i]]==0))
ans++,t1[b[i]]--,t2[c[i]]--,vis[i]=1,f=1;
}
}
while(f);
printf("%d\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现