把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【思维·观察·细节】删除 纪中集训

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;
}
posted @ 2019-08-08 10:02  Starlight_Glimmer  阅读(7)  评论(0编辑  收藏  举报  来源
浏览器标题切换
浏览器标题切换end