BZOJ3590 [Snoi2013]Quare
题意
4.20四川芦山地震发生后,抗震救灾委员会接到一个紧急任务,四川省给该委员会发了一份地图,这份地图给出了该省一些城市的情况:任两个城市是用一条或多条公路连接起来的,也可以没有公路连接,但是每个城市都可以直接或间接地到达另外的城市,注意这些公路是可以双向行驶的。由于最近余震、暴雨造成泥石流倾泻,使得车辆在这些公路上行驶很不安全,于是四川省决定尽快对部分公路进行抢修,以保障救援车辆行车安全。
该省对所有的公路情况都进行了勘察,分析估计了抢修某段公路所需要花费的时间,并记录在地图中。现在该省希望抗震救灾委员会能找到一个方案,该方案决定出哪些公路需要抢修,使得抢修后的公路仍能保证任意两个城市之间都能直接或间接地相连,同时为了安全起见,即使某一条抢修的公路被泥石流阻断了,任意两城市仍能保持这个性质。由于时间紧迫,抗震救灾委员会还需保证找到的这个方案总抢修时间最短。
\(n \leq 12, m \leq 40\)
分析
参照Lethelody的题解。
这道题大意就是:给出一个无向图.求一个权值最小的包含所有点的双联通子图.
定义一些状态:
\(f[i]\):集合状态为\(i\).且使在\(i\)中的点双联通的最小权值.
\(h[i][j][0]\):一个端点是\(j\).另一个端点在点集\(i\)中的边的最小权值.
\(h[i][j][1]\):一个端点是\(j\).另一个端点在点集\(i\)中的边的次小权值.
\(g[i][j][k]\).集合状态为\(i\).且使在\(i\)中的点构成一条链.两端点分别是\(j\)和\(k\)的最小权值.
容易发现.一个边双联通图.必然是由一个双联通的子图和一条链,链的两个端点连向这个连通子图构成的.那么就可以枚举这条链进行转移.
要注意的是:一个点是一个权值为0的双联通图.同时也是一条权值为0的链.
在转移的时候.如果这条链是一个点.转移方程是:
f[i] = min(f[i], f[t] + g[s][u][u] + h[t][u][0] + h[t][u][1]);
如果这条链的两端点不同.转移方程是:
f[i] = min(f[i], f[t] + g[s][u][v] + h[t][u][0] + h[t][v][0]);
先预处理出\(h\)和\(g\)数组就好了.
关于\(h\)数组的处理.直接暴力枚举就可以了.
关于\(g\)数组的处理.也是一个状压DP.枚举当前点集i能转移出的状态更新就好了.
时间复杂度\(O(3^n \cdot n^2)\)
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<cstring>
#include<cassert>
#define rg register
#define il inline
#define co const
template<class T>T read()
{
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return data*w;
}
template<class T>T read(T&x)
{
return x=read<T>();
}
using namespace std;
typedef long long ll;
co int INF=0x1f1f1f1f; // edit 2
co int MAXN=12,MAXM=40;
int n,m;
struct edge
{
int nx,fr,to,w;
}e[MAXM<<1]; // edit 1
int head[MAXN],ecnt;
void addedge(int x,int y,int w)
{
e[++ecnt].fr=x,e[ecnt].to=y,e[ecnt].w=w;
e[ecnt].nx=head[x],head[x]=ecnt;
// cerr<<"ecnt="<<ecnt<<endl;
// assert(-1<=e[ecnt].nx&&e[ecnt].nx<=ecnt);
}
int g[1<<MAXN][MAXN][MAXN],h[1<<MAXN][MAXN][2];
int f[1<<MAXN];
void init()
{
memset(g,0x1f,sizeof g);
for(int i=0;i<n;++i)
g[1 << i][i][i]=0;
for(int i=0;i<=ecnt;++i)
{
int x=e[i].fr,y=e[i].to,w=e[i].w;
int s = (1 << x) | (1 << y);
g[s][x][y] = min(g[s][x][y],w);
}
for(int i=1;i<(1<<n);++i)
for(int x=0;x<n;++x)
for(int y=0;y<n;++y)
if(((1 << x) | i) == i && ((1 << y) | i) == i)
for(int k=head[y];k!=-1;k=e[k].nx)
{
assert(-1<=k&&k<=ecnt);
int v=e[k].to,w=e[k].w;
if(((1 << v) | i) != i)
{
int s = (1 << v) | i;
// cerr<<"v="<<v<<endl;
// assert(s<(1<<n));
// assert(s<(1<<n)&&x<n&&v<n&&i<(1<<n)&&y<n);
g[s][x][v] = min(g[s][x][v],g[i][x][y] + w);
}
}
// cerr<<"g end"<<endl;
memset(h,0x1f,sizeof h);
for(int i=1;i<(1<<n);++i)
for(int x=0;x<n;++x)
if(((1 << x) | i) != i)
for(int j=head[x];j!=-1;j=e[j].nx)
{
int y=e[j].to,w=e[j].w;
if(((1 << y) | i) == i)
{
if(w<h[i][x][1])
{
h[i][x][1]=w;
if(h[i][x][1]<h[i][x][0])
swap(h[i][x][1],h[i][x][0]);
}
}
}
}
int cnt(int x)
{
int res=0;
while(x)
{
++res;
x-=(x&-x);
}
return res;
}
void solve()
{
memset(f,0x1f,sizeof f);
for(int i=0;i<n;++i)
f[1 << i] = 0;
for(int i=1;i<(1<<n);++i)
if(cnt(i)>=2)
for(int s=i&(i-1);s;s=(s-1)&i)
{
int t = i - s;
for(int x=0;x<n;++x)
for(int y=0;y<n;++y)
if(((1 << x) | s) == s && ((1 << y) | s) == s)
{
if(x == y)
f[i] = min(f[i],f[t] + g[s][x][x] + h[t][x][0] + h[t][x][1]);
else
f[i] = min(f[i],f[t] + g[s][x][y] + h[t][x][0] + h[t][y][0]);
}
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int T=read<int>();
while(T--)
{
memset(head,-1,sizeof head);
ecnt=-1;
read(n);read(m);
for(int i=0;i<m;++i)
{
int x=read<int>()-1,y=read<int>()-1,w=read<int>();
// cerr<<"x="<<x<<" y="<<y<<" w="<<w<<endl;
addedge(x,y,w);
addedge(y,x,w);
}
init();
// cerr<<"init end"<<endl;
solve();
if(f[(1 << n) - 1] < INF)
printf("%d\n",f[(1 << n) - 1]);
else
puts("impossible");
}
return 0;
}
Hint
注意初始值,如果赋成0x3f3f3f3f
加4个就会炸int。
BZOJ3590 [Snoi2013]Quare的更多相关文章
- 【BZOJ3590】[Snoi2013]Quare 状压DP
这道题...神题. 首先看到数据范围,一眼状压 dp .然后? 没了. 理性分析,这里说断掉任意一条边图依然连通,即整个图构成一个边双(而不是点双). 之前用 fire (机房里的随机算法总称)之所以 ...
- BZOJ 3590: [Snoi2013]Quare
首先有一个性质,一个双联通图一定可以拆成一个小的双联通子图和一条链 一个点可以视为权值为0的双联通图或者一个点的链 状压DP,枚举子集 O(3^n*n^2) #include<cstdio> ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- BZOJ3590 SNOI2013Quare(状压dp)
可能作为最优解的边双都可以这样生成:初始时边双内只有一个点,每次选取边双内部两点(可以相同)和一个当前不在边双内的点集,以该两点为起止点找一条链(当然如果两点相同就是个环)将点集串起来,加入边双.状压 ...
- [ SNOI 2013 ] Quare
Description 题目链接 求一张无向带权图的边双连通生成子图的最小代价. Solution 核心的思路是,一个点双连通分量肯定是一堆环的并. 考虑增量地构造这个边双连通图,每次把一个环并进去, ...
- MapReduce的C#实现及单元测试(试验)
MapReduce.cs类文件代码 MapReduce的执行方法 using System; using System.Collections.Generic; //using System.Lin ...
- 苹果新的编程语言 Swift 语言进阶(十四)--扩展
扩展是为一个已经存在的类.结构.枚举类型添加新功能的一种方式,包括为不能存取源代码的那些已经存在的类型添加功能. 扩展类似于Objective-C语言中的类别,与类别不同的是Swift语言的扩展没有名 ...
- F#注解
不要问我为啥要学F#——因为气质摆在那里 标注:以下内容均来自 anderslly F#系列 1.类型推演 let square x = x * x //接受一个某类型参数的quare函数返回一个这个 ...
- 对MBProgressHUD进行二次封装并精简使用
对MBProgressHUD进行二次封装并精简使用 https://github.com/jdg/MBProgressHUD 几个效果图: 以下源码是MBProgressHUD支持最新的iOS8的版本 ...
随机推荐
- 论Sava(),SaveOrUpdate(),Merge()区别
一.Save(): 用于将一个临时对象转变为持久化对象,也就是将一个新的业务实体保存到数据库中:相当于jdbc的insert. <假如两个实体之间有关系(例如employee表和address表 ...
- Linux 忘记密码解决方法,Linux 远程登录
一.Linux 忘记密码解决方法 很多朋友经常会忘记Linux系统的root密码,linux系统忘记root密码的情况该怎么办呢?重新安装系统吗?当然不用!进入单用户模式更改一下root密码即可. 步 ...
- 013PHP基础知识——流程控制(一)
<?php /** * 13 流程控制(一) * if语句: if(表达式){ 表达式 }elseif(表达式){ 代码段 } * if语句中,一个条件成立,其他分支不执行. * if中的表达式 ...
- MySQL数据中分级分组显示数据
前面已经有了SqlServer数据分级分组显示数据了.今天又来做一个MySQL数据库中的分级分组显示,SqlServer中用到了递归,这里为了简单就直接把根的数据显示为0 ,而不用递归了. 在MySQ ...
- URAL 1203 Scientific Conference 简单dp 难度:0
http://acm.timus.ru/problem.aspx?space=1&num=1203 按照结束时间为主,开始时间为辅排序,那么对于任意结束时间t,在此之前结束的任务都已经被处理, ...
- Unity 3D 无法显示中文的解决方法
大家开始用unity3D时想必都会遇到一个问题,使用中文时会乱码.这是由于编码方式不同导致的,具体解决方法如下: 程序写代码什么的最好下个像Notepad++类似的工具,这里使用Notepad++修改 ...
- 缓存LruCache简单创建和使用
LruCache一般使用: /** * 总容量为当前进程的1/8,单位:KB * sizeOf():计算缓存对象的大小,单位要一致 * entryRemoved():移除旧缓存时调用 */ int m ...
- Linux下设备的基本管理
一.系统中磁盘的管理 1.本地存储设备的识别 fdisk -l ## 真实存在的设备(带*为启动分区) cat /proc/partition ## 系统识别的设备 blkid ...
- 访问IO设备
http://blog.csdn.net/goodluckwhh/article/details/16986871 内存屏障主要解决的问题是编译器的优化和CPU的乱序执行.编译器在优化的时候,生成的汇 ...
- clean-css 安装 使用
https://github.com/jakubpawlowicz/clean-css-cli https://davidwalsh.name/clean-css