ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

省选补题记录

2021-04-03 07:32:09  阅读:156  来源: 互联网

标签:ch 记录 省选 sum times int cost 补题 include


把 \(15-20\) 年的省选题挨个写了写。

ps: 题目顺序是按这个菜鸡的通过时间来写的。

省选联考2020 DAY2T1 信号传递

题意描述

洛谷

给你一个长度为 \(n\) 的序列 \(s\), \(s_i\) 和 \(s_{i+1}\) 之间有三种传递方式:

  • \(s_{i+1}\) 在 \(s_{i}\) 的右边,花费为 \(p_{s_{i+1}}-p_{s_{i}}\)
  • \(s_{i+1}\) 在 \(s_i\) 的左边,花费为 \(k\times (p_{s_{i+1}}+s_{i})\)
  • \(s_{i+1}=s_i\) 则花费为 \(0\)

其中 \(p\) 是一个大小为 \(m\) 的排列,\(p_i\) 表示 \(i\) 在排列 \(p\) 中的位置。

现在让你确定一个排列 \(P\), 使得传递的花费最少。

数据范围:\(n\leq 10^5,m\leq 23\) 。

solution

毒瘤卡常状压题。

有一个很显然的状压 \(dp\) 的想法就是设 \(f[s]\) 表示已经确定了前 \(|s|\) 个位置上的数,且这些位置上的数的集合为 \(s\)。

其中 \(s\) 是一个 \(0/1\) 串,\(|s|\) 表示 \(s\) 中 \(1\) 的个数,若 \(s\) 的第 \(i\) 位为 \(1\) 则表示第 \(i+1\) 个数的位置已经被确定了,为 \(0\) 则相反) 。

设 \(t[i][j]\) 表示 \(s_k\) 为 \(i\) 且 \(s_{k+1}\) 为 \(j\) 的次数。这个在输入的时候就可以预处理出来。

转移则有:\(\displaystyle f[s\text{^} (1<<(i-1))] = \min(f[s] + (|s|+1)\times \sum_{j\in s} (k*t[i][j]+t[j][i])) + (|s|+1)\times \sum_{j\notin s} (k*t[j][i]+t[i][j])\)

解释一下:我们枚举第 \(|s|+1\) 位置上的数,设其为 \(i\), 那么 \(j\in s\) 则表示 \(j\) 在 \(i\) 的左边,反之 \(j\notin s\) 则 \(j\) 在 \(i\) 的右边。分情况讨论一下传递的代价:

  • \(j\) 在 \(i\) 的左边,传递方向为 \(i\rightarrow j\), 则代价为 \((|s|+1)\times k\times t[i][j]\)
  • \(j\) 在 \(i\) 的左边,传递方向为 \(j\rightarrow i\), 则代价为 \((|s|+1)\times t[j][i]\)
  • \(j\) 在 \(i\) 的右边,传递方向为 \(i\rightarrow j\) ,则代价为 \((|s|+1)\times t[i][j]\)
  • \(j\) 在 \(i\) 的右边,传递方向为 \(j-i\), 则代价为 \((|s|+1)\times k\times t[j][i]\)

我们从小到大枚举 \(s\), 就可以做到 \(O(m^22^m)\) 的时间复杂度,空间复杂度则为 \(O(m2^m)\) 。

然后我做到这里就不太会了,看了看题解才过了这道题。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
const int N = 1<<23;
int n,m,K,x,last;
int f[N],t[24][24];
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
int main()
{
	n = read(); m = read(); K = read();
	for(int i = 1; i <= n; i++)
	{
		x = read();
		if(i > 1) t[last][x]++;
		last = x;
	}
	memset(f,127,sizeof(f));
	f[0] = 0;
	for(int i = 0; i < (1<<m); i++)
	{
		int cnt = 0;
		for(int j = 1; j <= m; j++) if((i>>(j-1))&1) cnt++;
		for(int j = 1; j <= m; j++)
		{
			if(!((i>>(j-1))&1))
			{
				int sum = 0;
				for(int k = 1; k <= m; k++)
				{
					if((i>>(k-1))&1) sum += K*t[j][k], sum += t[k][j];  
					else if(j != k) sum += K*t[k][j], sum -= t[j][k];
				}		
				f[i^(1<<(j-1))] = min(f[i^(1<<(j-1))],f[i]+sum*(cnt+1));
			} 

		}
	}
	printf("%d\n",f[(1<<m)-1]);
	return 0;
}

考虑继续卡常优化一下。

设 \(cost(s,i)\) 为 \(\sum_{j\in s} (k*t[i][j]+t[j][i])) + \sum_{j\notin s} (k*t[j][i]+t[i][j])\) ,也就是 \(|s|+1\) 前的系数。

处理完这个 \(cost(s,i)\) 那么我们在转移 \(f\) 的时候复杂度就降为了 \(O(m2^m)\) 。

预处理 \(cost(s,i)\) 其实是可以 \(O(m2^m)\) 的做的。具体来说就是:

\(\displaystyle cost(0,i) = \sum_{j\neq i} k\times t[j][i] + t[i][j]\)

\(cost(s,i) = cost(s-(1<<(j-1),i) + t[i][j]\times (1+k) + t[j][i]\times (1-k)\) (其中 \(s\) 的第 \(j-1\) 位为 \(1\))。

解释一下: 对于 \(cost(0,i)\) 这时候 \(j(j\neq i)\) 都在 \(i\) 的右边,所以传递代价为 \(k\times t[j][i]+t[i][j]\) 。

由 \(s-(1<<(j-1))\) 到 \(s\) 相当于是 \(j\) 原来在 \(i\) 的右边,现在到了左边的位置,我们只需要把 \(j\) 在 \(i\) 的右边的代价减去,在加上 \(j\) 在 \(i\) 的左边的传递代价, 即 \(t[i][j]\times (1+k) + t[j][i]\times (1-k)\) ,然后我们就可以由 \(cost(s-(1<<(j-1)),i)\) 算出 \(cost(s,i)\) 。

因为每次都是由 \(s\) 减去一个数转移过来的,所以可以每次减去 \(lowbit(s)\) 转移。

那么递推 \(cost(s,i)\) 的复杂度就变为了 \(O(m2^m)\) 。

写完交上去之后,你会发现 \(\text{MLE}\) 了,毒瘤出题人卡了一手空间。

至于如何卡过去,可以看这篇 题解

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;
const int N = 1<<23;
int n,m,K,x,last;
int t[24][24],lg[N],num[N],f[N],cost[N/2][24];
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
int lowbit(int x){return x & (-x);}
int main()
{
	n = read(); m = read(); K = read();
	for(int i = 1; i <= n; i++)
	{
		x = read()-1;
		if(i > 1) t[last][x]++;
		last = x;
	}
	lg[0] = -1; num[0] = 0;
	for(int i = 1; i < (1<<m); i++)
	{
		lg[i] = lg[i>>1] + 1;
		num[i] = num[i>>1] + (i&1);
	}
	for(int i = 0; i < m; i++)
	{
		for(int j = 0; j < m; j++)
		{
			if(i != j) cost[0][i] += K * t[j][i] - t[i][j]; 
		}
		for(int j = 1; j < (1<<(m-1)); j++)
		{
			int k = lowbit(j), o = lg[k];
			if(o >= i) o++;
			cost[j][i] = cost[j^k][i] + t[i][o] * (1+K) + t[o][i] * (1-K);
		}
	}
	memset(f,127,sizeof(f));
	f[0] = 0;
	for(int i = 1; i < (1<<m); i++)
	{
		for(int j = i, y; y = lowbit(j); j ^= y)
		{
			int o = lg[y], s = i^y;
			f[i] = min(f[i],f[i^y]+num[i]*cost[s&y-1|s>>o+1<<o][o]);
		}
	}
	printf("%d\n",f[(1<<m)-1]);
	return 0;
}

标签:ch,记录,省选,sum,times,int,cost,补题,include
来源: https://www.cnblogs.com/genshy/p/14613054.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有