P3943 星空(状压DP+bfs+异或差分)
命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷。
你来过,然后你走后,只留下星空。
题目描述
逃不掉的那一天还是来了,小 F 看着夜空发呆。
天上空荡荡的,没有一颗星星——大概是因为天上吹不散的乌云吧。
心里吹不散的乌云,就让它在那里吧,反正也没有机会去改变什么了。
小 C 拿来了一长串星型小灯泡,假装是星星,递给小 F,想让小 F 开心一点。不过,有着强迫症的小 F 发现,这串一共 n 个灯泡的灯泡串上有 k 个灯泡没有被点亮。小 F 决定和小 C 一起把这个灯泡串全部点亮。
不过,也许是因为过于笨拙,小 F 只能将其中连续一段的灯泡状态给翻转——点亮暗灯泡,熄灭亮灯泡。经过摸索,小 F 发现他一共能够翻转 m 种长度的灯泡段中灯泡的状态。
小 C 和小 F 最终花了很长很长很长很长很长很长的时间把所有灯泡给全部点亮了。他们想知道他们是不是蠢了,因此他们找到了你,让你帮忙算算:在最优的情况下,至少需要几次操作才能把整个灯泡串给点亮?
解题思路
把未点亮的位置用1表示,点亮的位置用0表示
Step 1
首先有一些区间翻转操作,考虑怎么快速维护
有一种神奇的东东叫做异或差分,顾名思义就是每个位置和上个位置的异或值
例如原数列为
10010110
那么差分数列为
110111010
注意差分数列要到第n+1位
那么有了这个数列,在区间反转时只需要转换为对后面的影响
也就是在差分数组第l位异或一,在第r+1位异或一
最终把差分数组前缀异或出来就可以得到原数组
例如区间反转2~5,
则差分数组为100110010
得到的原数组为11101110
可以发现正好反转了
所以我们把题目给的原状态的异或差分数组求出,当差分数组全部为0时,可知此时原数组也全为0,也就是目标状态
Step 2
那么区间反转操作可以看成将序列中两个位置去翻,很容易得知如果这两个位置都是0,那么无意义
分两种讨论
1:一个为0,一个为1
那么取反之后还是一个为0,一个为1,但是交换了位置,也可以看成1移动了位置
2:都为1
直接消掉就可以了
于是我们可以预处理出消除某两个1的最少花费,也就是对于每种操作,在每个位置连一条边权为1的边,那么两点间的最短路就是最少花费,但是跑最短路有个log,较慢
我们知道边权为1的最短路和bfs是等价的,且bfs是没有log的,所以bfs就可以了
Step 3
回归正题,这个题是个状压DP
设F[S]表示要求点亮的位置的状态,由于是在异或差分数组中,所以要最多有$2\times k$个1
则状态转移就很简单了,就是选取两个没被点亮的把它点亮,取最小花费就可以啦
#include<bits/stdc++.h>
using namespace std;
const int N = 4e4+7;
int n,k,m;
int a[N],pos[700],opt[700],d[N];
int dis[N],len[200][200];
int f[1<<(18)],tot=0;
queue<int> q;
bool vis[N];
void bfs(int st)
{
for(int i=1;i<=n+1;i++)
{
dis[i]=99999999;
vis[i]=0;
}
dis[st]=0;
vis[st]=1;
q.push(st);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=1;i<=m;i++)
{
int l=x-opt[i];
int r=x+opt[i];
if(l>=1&&!vis[l])
{
dis[l]=dis[x]+1;
vis[l]=1;
q.push(l);
}
if(r<=n+1&&!vis[r])
{
dis[r]=dis[x]+1;
vis[r]=1;
q.push(r);
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=k;i++)
{
int x;
scanf("%d",&x);
a[x]=1;
}
for(int i=1;i<=n+1;i++)
{
d[i]=a[i-1]^a[i];
if(d[i]) pos[++tot]=i;
}
for(int i=1;i<=m;i++)
scanf("%d",&opt[i]);
for(int i=1;i<=tot;i++)
{
bfs(pos[i]);
for(int j=1;j<=tot;j++)
{
len[i][j]=dis[pos[j]];
}
}
memset(f,12,sizeof(f));
f[(1<<tot)-1]=0;
for(int i=(1<<tot)-2;i>=0;i--)
{
for(int x=1;x<=tot;x++)
{
if(!((i>>(x-1))&1))
{
for(int y=x+1;y<=tot;y++)
{
if(!((i>>(y-1))&1))
{
f[i]=min(f[i],f[i+(1<<(x-1))+(1<<(y-1))]+len[x][y]);
}
}
}
}
}
cout<<f[0];
return 0;
}
原文地址:https://www.cnblogs.com/jesoyizexry/p/15130048.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Mybatis学习笔记(一)
- mybatis文件映射之鉴别器discriminator标签
- LeetCode刷题总结 -- 链表篇
- LeetCode刷题总结 -- 数组篇
- 剑指offer(07-09)题解
- 学以致用C++设计模式 之 “适配器模式”
- FTP文件管理项目(本地云)项目日报(九)
- 【Linux】血泪教训 -- 动态链接库配置方法
- FTP文件管理项目(本地云)项目日报(八)
- FTP文件管理项目(本地云)项目日报(七)
- FTP文件管理项目(本地云)项目日报(六)
- Transformers Assemble(PART I)
- FTP文件管理项目(本地云)项目日报(五)
- 几个Python“小伎俩”
- FTP文件管理项目(本地云)项目日报(四)