7.17牛客多校第一场
签到题BDF
K
给定两个数组,一个数组是a={0,1,2,3...,n-1},另一个是b={b0,b1,b2,b3...bn-1},要求给b数组定一个排列方式,使得∑sqrt(abs(ai-bi))尽可能小。
n<=1000
数据100组到500组,b数组完全随机生成。
多组数据总误差在4%以内可被接受。
题解:
一开始是直接对b排序,依次填,但是不够优秀,因为这样会产生很多1,在根号下,这样是很亏的。
规避1,就先尽可能让a中的第bi位与bi配对。
剩下的,我采用的是顺序填,然后再两两判断交换后是否更优。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int a[50000],b[50000],c[50000],aa[50000],d[50000]; 4 int main() 5 { 6 freopen("test.in","r",stdin); 7 int t; 8 scanf("%d",&t); 9 while (t) 10 { 11 t--; 12 int n; 13 scanf("%d",&n); 14 for (int i=0;i<=n;i++) b[i]=0; 15 int k=0; 16 for (int i=1;i<=n;i++) 17 { 18 scanf("%d",&a[i]); 19 if (b[a[i]]) c[++k]=a[i]; 20 b[a[i]]=1; 21 } 22 sort(c+1,c+k+1); 23 int l=0; 24 for (int i=0;i<=n;i++) if (b[i]) aa[i]=i; 25 for (int i=0;i<n;i++) if (!b[i]) 26 { 27 l++; 28 d[l]=i; 29 } 30 double aa1=1000000000,aa2=0; 31 int aj=1; 32 for (int i=1;i<=l;i++) 33 { 34 aa2=0; 35 for (int j=1,p=i;j<=l;j++,p++) 36 { 37 if (p==l+1) p=p-l; 38 aa2=aa2+sqrt(abs(d[j]-c[p])); 39 } 40 if (aa2<=aa1) 41 { 42 aa1=aa2; 43 aj=i; 44 } 45 } 46 aj=1; 47 for (int j=1,p=aj;j<=l;j++,p++) 48 { 49 if (p==l+1) p=p-l; 50 aa[d[j]]=c[p]; 51 } 52 for (int i=0;i<n;i++) 53 { 54 for (int j=i+1;j<n;j++) 55 { 56 if (aa[i]==i||aa[j]==j) continue; 57 if (sqrt(abs(aa[i]-i))+sqrt(abs(aa[j]-j))>=sqrt(abs(aa[j]-i))+sqrt(abs(aa[i]-j))) swap(aa[i],aa[j]); 58 } 59 } 60 for (int i=n-1;i>=0;i--) 61 { 62 for (int j=i-1;j>=0;j--) 63 { 64 if (aa[i]==i||aa[j]==j) continue; 65 if (sqrt(abs(aa[i]-i))+sqrt(abs(aa[j]-j))>sqrt(abs(aa[j]-i))+sqrt(abs(aa[i]-j))) swap(aa[i],aa[j]); 66 } 67 } 68 for (int i=0;i<n-1;i++) printf("%d ",aa[i]); 69 printf("%d\n",aa[n-1]); 70 } 71 }
G
两个长度为n的数组a和b,必须对a数组进行k次两两交换,最大化∑abs(a[i]-b[i])
n<=5*10^5 k<=10^8
-10^8<=a[i],b[i]<=10^8
题解:
妙题一个
绝对值化成正号负号挂在数的前面。
a数组和b数组的正号个数总数与负号个数总数相同时,一定对应了一种合法方案。
tips:当然,有可能不合法,因为可能给大数分配负号,小数分配正号,导致绝对值取反了,不过这样的分配方式不会使答案更优,因此不会考虑。
这样的话,我们将a和b中所有数排序,前n大给正号,后n个给负号,这样的话,就会得到一个在k取足够大的情况下的最优解。
结论:恰好k次等价于最多k次
证明:除了n=2的情况,每次交换两个赋予负号相同的位置,不影响结果,因此恰好k次等价于最多k次。
再把最多k次这个限制考虑进来,每次交换如果要使得答案增加,那一定是交换这样的负号对
a={+,-}
b={+,-}
交换之后答案必然增加。
增加的答案为2*(min(a+,b+)-max(a-,b-))
这样的话,对min(a[i],b[i])和max(a[i],b[i])分别排序,再贪心即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e5+10; 4 int a[N],b[N],c[N],d[N]; 5 int main() 6 { 7 int n,k; 8 scanf("%d%d",&n,&k); 9 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 10 for (int i=1;i<=n;i++) scanf("%d",&b[i]); 11 if (n==2) 12 { 13 if (k&1) swap(a[1],a[2]); 14 printf("%d\n",abs(a[1]-b[1])+abs(a[2]-b[2])); 15 return 0; 16 } 17 long long ans=0; 18 for (int i=1;i<=n;i++) 19 { 20 ans=ans+abs(a[i]-b[i]); 21 c[i]=max(a[i],b[i]); 22 d[i]=min(a[i],b[i]); 23 } 24 sort(c+1,c+n+1); 25 sort(d+1,d+n+1); 26 for (int i=1;i<=min(n,k);i++) if (-c[i]+d[n-i+1]>0) ans=ans+2*(d[n-i+1]-c[i]); 27 printf("%d\n",ans); 28 }

浙公网安备 33010602011771号