• 【题解】 「联合省选2020」信号传递 状压dp LOJ3302


    Legend

    Link \(\textrm{to LOJ}\)

    original

    你有 \(m\ (1 \le m \le 23)\) 个信号站,你可以任意安排它们之间的顺序。

    再给定一个长度为 \(n\ (1 \le n \le 10^5)\) 的数组 \(S\) 表示信号站之间要进行 \(n-1\) 次通讯,分别是 \(S_i \to S_{i+1}\ (i <n)\)。如果两个分别位于 \(a,b\) 号位置上的信号塔要进行 \(a\to b\) 传输,那么根据限制条件只能使用两种通讯方式之一:

    • \(a\le b\),花费 \(b-a\)的代价传输。
    • \(a >b\),花费 \(k(b+a)\) 的代价传输,\(k\ (1 \le k \le 100)\) 为给定常数。

    请求出最优情况下代价的最小值。

    simplified

    给定一个 \(m\times m\ (1 \le m \le 23)\) 的二维数组 \(v\ (\sum v \le 10^5-1)\)。和一个常数 \(k\ (1 \le k \le 100)\)。对于一个 \(1\sim m\) 的排列 \(p\),我们可以求出它的花费为:

    \[\sum_{i=1}^{m}\sum_{j=i+1}^m v_{p_i,p_j}\times(j-i)+k\times v_{p_j,p_i}\times(j+i) \]

    请求出最优情况下花费的最小值。

    Editorial

    作为 联合省选2020 的第四题,是一道良心送温暖题,这个套路我在冲刺 \(\textrm{TG}\) 组的时候就有所耳闻,让我们一起为出题人点赞。

    fake solution

    \(m\) 出乎意料的小,于是考虑状压。

    一种很 \(\textrm{naive}\) 的想法是设 \(dp_{st}\) 表示从左到右 \(\textrm{bitcount(st)}\) 个信号站已经放了 \(st\) 集合里的所有信号站。转移直接枚举一个新信号站,加上别人到他的贡献(第一种传输)和他到前面的贡献(第二种传输)。

    但你发现这两种贡献不好保存计算,因为你必须知道他们之间的距离,而在这个数据范围下是不可以状压的。

    这意味着不能加入一个信号站的时候计算所有与它相关的贡献,以上 \(\textrm{dp}\) 转移假了。

    山重水复疑无路。

    real solution

    part 1

    不妨我们只考虑第一种传输,即只有从左往右的。

    假设我们有 \(黑 \to 绿\) 的一条传输,那么只要 \(绿\) 没有出现,\(黑\) 出现了,就一直会造成贡献。也就是每放一个新信号塔,只要 \(绿\) 没出现,那么就会造成 \(1\) 的贡献,造成的总贡献就是它们之间的距离。

    那么就说明每一对 \((\)出现过的,没出现的\()\),且有传输要求的都会在放置一个新的信号塔后产生 \(1\) 的贡献。

    由上图来看,我们就根本不需要记录他们的出现位置了。

    part 2

    再考虑从第二种传输方法。我们把它拆成两部分:

    右半部分紫色的贡献可以直接与 \(\textrm{part 1}\) 同时计算。

    左半部分蓝色的贡献直接在加入新信号塔的时候计算。

    Code

    代码不算太难,主要要预处理以下东西:

    • \(cb_{st}\) 表示当前放了 \(st\) 集合的信号塔,如果放一个新的,造成的 \(\textrm{part 1}\) 代价与 \(\textrm{part 2}\) 蓝色部分线路代价之和。
      • 枚举当前选了哪一个既可以转移,复杂度 \(O(2^mm)\)
    • \(cf_{i,st}\) 表示当前放了 \(st\) 集合的信号塔,如果下一个放 \(i\) 信号塔,造成的 \(\textrm{part 2}\) 蓝色线路有多少次单程路线。
      • 为什么要这么记呢,因为我们没有办法记录总长度。但因为左半部分蓝色的贡献直接在加入新信号塔的时候计算,这个时候蓝色单程的长度是确定的,所以乘上 \(k\) 和路线长度就可以了。
      • 容易发现 \(cf\) 数组直接开是开不下的。\(st\) 的集合相互之间没有影响,所以可以把集合拆成两个小集合,变成 \(cf1_{i,st}\)\(cf2_{i,st}\),分开算贡献。
      • 复杂度 \(O(2^{\frac{m}{2}}m^2)\)

    \(dp\) 复杂度是 \(O(2^mm)\),所以总复杂度 \(O(2^mm)\),可以稳稳当当通过此题。

    空间复杂度 \(O(2^m+m^2)\),可以稳稳当当通过此题。

    // Author : Imakf
    #include <bits/stdc++.h>
    
    #define int unsigned
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9')
    		x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    void cm(int &a ,int b){a = a < b ? a : b;}
    
    const int MX = 23;
    int cnt[MX][MX];
    int lg2[1 << MX];
    int dp[1 << MX];
    
    int cb[1 << MX];
    // 考虑以这个状态新放一个,消耗的普通传递代价以及特殊传递的前半段代价
    int cf1[23][1 << 12] ,cf2[23][1 << 12];
    // 考虑以这个状态新放一个 i,i 要造成多少次特殊传递
    const int base = (1 << 12) - 1;
    
    signed main(){
    	int n = read() ,m = read() ,k = read();
    	for(int i = 0 ,last = -1 ,now ; i < n ; ++i ,last = now){
    		now = read() - 1;
    		if(~last) ++cnt[last][now];
    	}
    	for(int i = 0 ; i < m ; ++i) cnt[i][i] = 0 ,lg2[1 << i] = i;
    
    	for(int i = 1 ; i < 1u << m ; ++i){
    		int add = lg2[i & -i];
    		cb[i] = cb[i ^ (i & -i)];
    		for(int j = 0 ; j < m ; ++j){
    			if((i >> j) & 1) cb[i] -= cnt[j][add] + k * cnt[add][j];
    			else cb[i] += cnt[add][j] + k * cnt[j][add];
    		}
    	}
    	for(int i = 0 ; i < 1 << 12 ; ++i){
    		for(int j = 0 ; j < m ; ++j){
    			for(int s = 0 ; s < 12 ; ++s){
    				if((i >> s) & 1) continue;
    				cf1[j][i] += cnt[s][j];
    			}
    			for(int s = 12 ; s < m ; ++s){
    				if((i >> (s - 12)) & 1) continue;
    				cf2[j][i] += cnt[s][j];
    			}
    		}
    	}
    	
    	for(int i = 1 ; i < 1u << m ; ++i){
    		int cnt1 = 0 ,cpi = i;
    		while(cpi) cpi -= cpi & -cpi ,++cnt1;
    		dp[i] = UINT_MAX;
    		for(int j = 0 ; j < m ; ++j){
    			if(((i >> j) & 1) ^ 1) continue;
    			cm(dp[i] ,dp[i ^ (1 << j)]
    					+ cb[i ^ (1 << j)] 
    					+ (cf1[j][i & base] + cf2[j][i >> 12]) * cnt1 * 2 * k);
    
    	}
    	printf("%d\n" ,dp[(1 << m) - 1]);
    	return 0;
    }
    
  • 相关阅读:
    练习题
    练习
    2.15
    数组
    java聊天工具12.4
    11.13(2)
    11.13
    10.30 作业
    10.23
    面向对象
  • 原文地址:https://www.cnblogs.com/imakf/p/13641197.html
Copyright © 2020-2023  润新知