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 }

 

posted @ 2021-07-18 09:21  praying_cqf  阅读(86)  评论(0)    收藏  举报