\(\frak{Description}\)

\(\rm Link.\)

\(\frak{Solution}\)

只会三十分,考试的时候冲了一发模拟退火 —— \(\rm rnm\),还是三十分!打开题解:"一个显然的结论",焯!

\(\text{Method 0}\)

朴素的想法是枚举信号站的顺序,复杂度 \(\mathcal O(n\cdot m!)\). 事实上,我们可以观察到当 \(n\) 较大时,有 \(m^2\) 远小于 \(n\),所以不妨换一个思路:预处理出 \(i\) 号信号站传递信息给 \(j\) 号信号站的次数 \(\text{cnt}_{i,j}\),这样复杂度就降低到 \(\mathcal O(n+m^2\cdot m!)\).

具体地,令 \(i\) 号信号站的位置为 \(p_i\),那么有单次 \(i\rightarrow j\) 的花费

\[c(i,j)=\begin{cases} p_j-p_i\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p_i\le p_j\\ k\cdot (p_i+p_j)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p_i>p_j \end{cases} \]

观察到每一项的系数实际上与 \(p_i,p_j\) 的相对关系有关,我们可以想出这样一个 \(\mathtt{dp}\):令 \(dp_{s}\) 表示考虑前 \(|s|\) 个位置,集合 \(s\) 的信号站放在这些位置上的答案。转移就有

\[dp_s=\min_{ i\in s} \left\{ dp_{s\setminus \{i\}}+p_i\cdot \sum_{j\in s\setminus \{i\}}(\text{cnt}_{j,i}+\text{cnt}_{i,j}\cdot k)+p_i\cdot \sum_{j\in U\setminus s}(-\text{cnt}_{i,j}+\text{cnt}_{j,i}\cdot k)\right \} \]

复杂度降低到了 \(\mathcal O(m^2\cdot 2^m)\). 其中 \(p_i\) 就是 \(s\)\(1\) 的个数。

考虑优化,我们尝试扔掉 \(j\) 那一层循环,令 \(g(s,i)\) 为对应 \(s,i\) 的转移加值(忽略 \(p_i\)),考虑经典地用最低位(不妨设 \(s\) 最低位为第 \(j\) 位)转移

\[g(s,i)=g(s\oplus 2^j,i)+\text{cnt}_{j,i}+\text{cnt}_{i,j}\cdot k-(-\text{cnt}_{i,j}+\text{cnt}_{j,i}\cdot k) \]

现在复杂度就变成了 \(\mathcal O(m\cdot 2^m)\),但是 \(g\) 的空间仍然过大,接下来讨论 \(g\) 的优化方案。

\(\text{Method 1}\)

其实,上面内存算出来 \(\text{736 mb}\),除以二就在可接受的范围内了嘛。由于 \(g(s,i)\) 合法的值一定满足 \(i\in s\),所以只需要开 \(23\cdot 2^{22}\) 的内存,可以卡过这道题。

\(\text{Method 2}\)

我们还可以利用一个性质:将二进制计数器加 \(n\) 次,二进制位变化次数在 \(\mathcal O(n)\) 级别。

所以还可以只开 \(g(i)\),从小到大枚举 \(s\) 的时候顺带计算 \(g(s,i)\). 比如从 \(s-1\) 转移到 \(s\) 就可以遍历 \((s-1)\oplus s\) 的所有为 \(1\) 的位置(也就是变化位),然后加加减减就行了。

\(\frak{Code}\)

\(\text{Method 1}\)

# include <cctype>
# include <cstdio>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while(!isdigit(s=getchar())) f|=(s=='-');
	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
	return f? -x: x;
}
template <class T>
inline void write(T x) {
	static int writ[50], w_tp=0;
	if(x<0) putchar('-'), x=-x;
	do writ[++w_tp]=x-x/10*10, x/=10; while(x);
	while(putchar(writ[w_tp--]^48), w_tp);
}

# include <cstring>
# include <iostream>
using namespace std;

const int maxm = 33;

int n,m,k,c[maxm][maxm];
int dp[1<<23],g[23][1<<22];

int main() {
    memset(dp,0x3f,sizeof dp);
    n=read(9), m=read(9), k=read(9);
    int x, pre, lim=(1<<m), hal=(lim>>1);
    for(int i=1; i<=n; ++i, pre=x) {
        x=read(9)-1;
        if(i^1) ++ c[pre][x];
    }
    for(int i=0;i<m;++i) {
        for(int j=0;j<m;++j) if(i^j)
            g[i][0] += c[j][i]*k-c[i][j];
        for(int j=1;j<hal;++j) {
            int to = __builtin_ctz(j); to+=(to>=i);
            g[i][j] = g[i][j^(j&-j)]+c[i][to]*(1+k)+c[to][i]*(1-k);
        }
    }
    dp[0]=0;
    for(int s=1;s<lim;++s) 
        for(int t=s; t; t^=(t&-t)) {
            int to = __builtin_ctz(t), bit = (t&-t);
            dp[s] = min(dp[s],dp[s^bit]+g[to][(s>>to+1<<to)|(s&bit-1)]*__builtin_popcount(s));
        }
    print(dp[lim-1],'\n');
    return 0;
}

\(\text{Method 2}\)

# include <cctype>
# include <cstdio>
# define print(x,y) write(x), putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while(!isdigit(s=getchar())) f|=(s=='-');
	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
	return f? -x: x;
}
template <class T>
inline void write(T x) {
	static int writ[50], w_tp=0;
	if(x<0) putchar('-'), x=-x;
	do writ[++w_tp]=x-x/10*10, x/=10; while(x);
	while(putchar(writ[w_tp--]^48), w_tp);
}

# include <cstring>
# include <iostream>
using namespace std;

const int maxm = 33;

int n,m,k,c[maxm][maxm];
int dp[1<<23],g[23];

int main() {
    memset(dp,0x3f,sizeof dp);
    n=read(9), m=read(9), k=read(9);
    int x, pre, lim=(1<<m);
    for(int i=1; i<=n; ++i, pre=x) {
        x=read(9)-1;
        if(i^1) ++ c[pre][x];
    }
    for(int i=0;i<m;++i) {
        for(int j=0;j<m;++j) if(i^j)
            g[i] += c[j][i]*k-c[i][j];
    }
    dp[0]=0;
    for(int s=1;s<lim;++s) {
		int coe = __builtin_popcount(s);
		for(int t=s^(s-1); t; t^=(t&-t)) {
			int to = __builtin_ctz(t), bit = (t&-t);
			if(bit&(s-1)) {
				for(int i=0;i<m;++i) if(i^to) g[i] -= c[i][to]*(1+k)+c[to][i]*(1-k);
			} else for(int i=0;i<m;++i) if(i^to) g[i] += c[i][to]*(1+k)+c[to][i]*(1-k);
		}
        for(int t=s; t; t^=(t&-t)) {
            int to = __builtin_ctz(t), bit = (t&-t);
            dp[s] = min(dp[s],dp[s^bit]+g[to]*coe);
        }
	}
    print(dp[lim-1],'\n');
    return 0;
}
posted on 2020-02-20 11:25  Oxide  阅读(101)  评论(0编辑  收藏  举报