本文主要是介绍fzy czn 生日赛 Journey题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
题目大意
给出 n × n n\times n n×n的二维数组 a a a,你要选定一个长度为 n n n的排列 p p p,求 ∑ i = 1 n ∑ j = 1 n ∣ p i − p j ∣ × a i , j \sum\limits_{i=1}^n\sum\limits_{j=1}^n|p_i-p_j|\times a_{i,j} i=1∑nj=1∑n∣pi−pj∣×ai,j的最小值。
1 ≤ n ≤ 21 , 1 ≤ a i , j ≤ 2 × 1 0 5 1\leq n\leq 21,1\leq a_{i,j}\leq 2\times 10^5 1≤n≤21,1≤ai,j≤2×105
时间限制 1000 m s 1000ms 1000ms,空间复杂度 512 M B 512MB 512MB。
题解
∣ p i − p j ∣ |p_i-p_j| ∣pi−pj∣显然阻碍了我们求最小值,我们令 q p i = i q_{p_i}=i qpi=i,那么求出一个排列 q q q即可对应到一个唯一的排列 p p p。
那么原式变为
∑ i = 1 n ∑ j = 1 n ∣ i − j ∣ × a q i , q j \sum\limits_{i=1}^n\sum\limits_{j=1}^n|i-j|\times a_{q_i,q_j} i=1∑nj=1∑n∣i−j∣×aqi,qj
分为 i < j i<j i<j和 i > j i>j i>j两种情况可得
∑ i = 1 n ∑ j = 1 i − 1 ( i − j ) × a q i , q j + ∑ i = 1 n ∑ j = i + 1 n ( j − i ) × a q i , q j \sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}(i-j)\times a_{q_i,q_j}+\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n(j-i)\times a_{q_i,q_j} i=1∑nj=1∑i−1(i−j)×aqi,qj+i=1∑nj=i+1∑n(j−i)×aqi,qj
后半部分 ∑ i = 1 n ∑ j = i + 1 n ( j − i ) × a q i , q j = ∑ j = 1 n ∑ i = 1 j − 1 ( i − j ) × a q i , q j = ∑ i = 1 n ∑ j = 1 i − 1 ( i − j ) × a q j , q i \sum\limits_{i=1}^n\sum\limits_{j=i+1}^n(j-i)\times a_{q_i,q_j}=\sum\limits_{j=1}^n\sum\limits_{i=1}^{j-1}(i-j)\times a_{q_i,q_j}=\sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}(i-j)\times a_{q_j,q_i} i=1∑nj=i+1∑n(j−i)×aqi,qj=j=1∑ni=1∑j−1(i−j)×aqi,qj=i=1∑nj=1∑i−1(i−j)×aqj,qi
那么原式为
∑ i = 1 n ∑ j = 1 i − 1 ( i − j ) × ( a q i , q j + a q j , q i ) = ∑ i = 1 n i ∑ j = 1 i − 1 ( a q i , q j + a q j , q i ) − ∑ j = 1 n j ∑ i = 1 j − 1 ( a q i , q j + a q j , q i ) = ∑ i = 1 n i × [ ∑ j = 1 i − 1 ( a q i , q j + a q j , q i ) − ∑ j = i + 1 n ( a q i , q j + a q j , q i ) ] \begin{aligned} &\sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}(i-j)\times (a_{q_i,q_j}+a_{q_j,q_i}) \\ =&\sum\limits_{i=1}^ni\sum\limits_{j=1}^{i-1}(a_{q_i,q_j}+a_{q_j,q_i})-\sum\limits_{j=1}^nj\sum\limits_{i=1}^{j-1}(a_{q_i,q_j}+a_{q_j,q_i}) \\ =&\sum\limits_{i=1}^ni\times [\sum\limits_{j=1}^{i-1}(a_{q_i,q_j}+a_{q_j,q_i})-\sum\limits_{j=i+1}^n(a_{q_i,q_j}+a_{q_j,q_i})] \end{aligned} ==i=1∑nj=1∑i−1(i−j)×(aqi,qj+aqj,qi)i=1∑nij=1∑i−1(aqi,qj+aqj,qi)−j=1∑nji=1∑j−1(aqi,qj+aqj,qi)i=1∑ni×[j=1∑i−1(aqi,qj+aqj,qi)−j=i+1∑n(aqi,qj+aqj,qi)]
也就是要求一个排列 q q q,使得这个式子最小。
我们假设当前已经选出了前 i − 1 i-1 i−1个 q q q值,现在要将 q i q_i qi赋为 k k k,求 q i q_i qi对答案的贡献 t m p tmp tmp。对于 1 ≤ j < i 1\leq j<i 1≤j<i,则 t m p tmp tmp加上 ( a k , q j + a q j , k ) (a_{k,q_j}+a_{q_j,k}) (ak,qj+aqj,k);如果 i < j ≤ n i<j\leq n i<j≤n,则 t m p tmp tmp减去 ( a k , q j + a q j , k ) (a_{k,q_j}+a_{q_j,k}) (ak,qj+aqj,k)。最后将 t m p tmp tmp乘上 i i i即可得到 q i q_i qi对答案的贡献。
我们考虑用状压 D P DP DP。设 S S S表示当前已经加入排列 q q q的数, f S f_S fS表示 S S S对应的贡献。那么当每次加入一个 k k k时,可以 O ( n ) O(n) O(n)计算贡献。总共可以选择 O ( n ) O(n) O(n)个 k k k值,那么总时间复杂度为 O ( n 2 2 n ) O(n^22^n) O(n22n)。常数比较小,而且跑不满,所以这是可以过的。
code
#include<bits/stdc++.h>
using namespace std;
int n,ct[1<<21];
long long a[25][25],f[1<<21];
int lb(int i){return i&(-i);
}
long long pt(int s,int wt){int p=ct[s]+1;long long tmp=0;for(int i=1;i<=n;i++){if(i==wt) continue;if((s>>i-1)&1) tmp+=a[wt][i]+a[i][wt];else tmp-=a[wt][i]+a[i][wt];}tmp*=p;return tmp;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%lld",&a[i][j]);}}f[0]=0;for(int s=1;s<1<<n;s++){f[s]=1e18;ct[s]=ct[s^lb(s)]+1;for(int i=1;i<=n;i++){if(!((s>>i-1)&1)) continue;f[s]=min(f[s],f[s^(1<<i-1)]+pt(s^(1<<i-1),i));}}printf("%lld",f[(1<<n)-1]);return 0;
}
这篇关于fzy czn 生日赛 Journey题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!