【思维·观察·细节】删除 纪中集训
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;
}