http://codeforces.com/contest/864/problem/D
题意:
给出n和n个数(ai <= n),要求改变其中某些数,使得这n个数为1到n的一个排列,首先保证修改字数最少,其次保证这个排列的字典序最小。
思路:
首先统计未出现过的数的个数,那么这个就是最小的改变次数。
将未出现过的数按升序存进一个数组C。
之后对于每一个数,如果这个数只出现过一次,那么就无法改变这个数。
如果一个数出现过大于一次,那么拿这个数x与C数组的第一个数字y比较,如果x<y 并且x未被标记,那么就标记x被保留过,在下一次遇到x的时候直接替换就可以了。
如果x > y,那么肯定用y去替换x,并且将x的出现次数减去1。
通过上述两个操作,就可以保证字典序最小。
代码:
1 #include <stdio.h> 2 3 int a[200005],b[200005],c[200005]; 4 bool v[200005]; 5 6 int main() 7 { 8 int n; 9 10 scanf("%d",&n); 11 12 for (int i = 1;i <= n;i++) 13 { 14 scanf("%d",&a[i]); 15 b[a[i]]++; 16 } 17 18 int cnt = 0; 19 20 for (int i = 1;i <= n;i++) 21 { 22 if (!b[i]) c[cnt++] = i; 23 } 24 25 int pos = 0; 26 27 for (int i = 1;i <= n;i++) 28 { 29 int x = a[i]; 30 31 if (pos == cnt) continue; 32 33 if (b[x] <= 1) continue; 34 35 int y = c[pos]; 36 37 if (!v[x]) 38 { 39 if (x < y) 40 { 41 v[x] = 1; 42 } 43 else 44 { 45 b[x]--; 46 pos++; 47 a[i] = y; 48 } 49 } 50 else 51 { 52 b[x]--; 53 a[i] = y; 54 pos++; 55 } 56 } 57 58 printf("%d ",cnt); 59 60 for (int i = 1;i <= n;i++) 61 { 62 if (i == 1) printf("%d",a[i]); 63 else printf(" %d",a[i]); 64 } 65 66 return 0; 67 }