最小生成树是最小斯坦纳树的一种特殊情况

最小生成树是在给定的点集和边中寻求最短网络使所有点连通

而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小

BZOJ2595

题意是给定一个棋盘图。告诉你景点的位置

然后让你在一些格子上放置指定数量的志愿者使得景点之间可以连通

问你最少需要派遣多少枚志愿者

典型斯坦纳树问题

令f[i][j][k]表示已经连接的景点的集合为k时,包含点a[i][j]的最小值

集合k的引入标志着这是一种状压DP

即以(i,j)为根时,景点集合为k时的斯坦纳树。然后有两种转移

1.由两个子集合并得到集合k,即f[i][j][k]=f[i][j][x]+f[i][j][y]-a[i][j],x|y=k(用景点枚举子集)

2.由根的转移得到,即f[i][j][k]=f[x][y][k]+a[i][j],其中(i,j)和(x,y)相邻(最短路直接SPFA,同层之间的转移有环?)

 #include<cstdio>
#include<queue>
//#include<iostream>
using namespace std;
const int INF=;
const int maxn=;
const int maxm=;
int n,m,K;
int bin[];
bool inq[maxn][maxm],vis[maxn][maxm];
int a[maxn][maxm];
int f[maxn][maxm][];
int dx[]={,,,-};
int dy[]={,-,,};
queue<pair<int,int> > q;
struct Data
{
int fit,sed,thd;
}pre[maxn][maxm][];
inline int read()
{
int x=;char ch=getchar();
while(ch>''||ch<'') ch=getchar();
while(ch>=''&&ch<='') {x=x*+ch-'';ch=getchar();}
return x;
}
void spfa(int sta)
{
//cout<<"###"<<sta<<endl;
while(!q.empty())
{
int x=q.front().first,y=q.front().second;
inq[x][y]=;q.pop();
//cout<<x<<" "<<y<<endl;
for(int k=;k<;k++)
{
int nx=x+dx[k],ny=y+dy[k];
if(x<||y<||x>n||y>m) continue;
if(f[nx][ny][sta]>f[x][y][sta]+a[nx][ny])
{
f[nx][ny][sta]=f[x][y][sta]+a[nx][ny];
pre[nx][ny][sta]=(Data){x,y,sta};
if(!inq[nx][ny])
{
q.push(make_pair(nx,ny));
inq[nx][ny]=;
}
}
}
}
}
void dfs(int x,int y,int sta)
{
if(x>INF||pre[x][y][sta].thd==) return;
vis[x][y]=;
Data t=pre[x][y][sta];
dfs(t.fit,t.sed,t.thd);
if(t.fit==x&&t.sed==y) dfs(x,y,sta-t.thd);
}
int main()
{
bin[]=;
for(int i=;i<;i++) bin[i]=bin[i-]<<;
n=read();m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
a[i][j]=read();
if(a[i][j]==) K++;
}
//令f[i][j][k]表示已经连接的景点的集合为k时
//包含点a[i][j]的最小值
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
for(int k=;k<bin[K];k++)
f[i][j][k]=pre[i][j][k].fit=INF;
K=;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]==)
f[i][j][bin[K]]=,K++;
//最初始是one-hot,集合中只有自己景点
for(int sta=;sta<bin[K];sta++)
{
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
for(int s=sta&(sta-);s;s=sta&(s-))
{
int t=f[i][j][s]+f[i][j][sta-s]-a[i][j];
if(t<f[i][j][sta])
{
f[i][j][sta]=t;
pre[i][j][sta]=(Data){i,j,s};
}
}
if(f[i][j][sta]<INF)
q.push(make_pair(i,j)),inq[i][j]=;
}
}
spfa(sta);
}
int x,y;
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(a[i][j]==)
{
x=i;y=j;break;
}
dfs(x,y,bin[K]-);
printf("%d\n",f[x][y][bin[K]-]);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
if(a[i][j]==) printf("x");
else if(vis[i][j]) printf("o");
else printf("_");
}
puts("");
}
return ;
}

这个题写完之后有一种莫名的打击感

最新文章

  1. C# 系统错误日志处理类
  2. virtualBox下面安装linux系统如何共享目录
  3. HBase、Redis、MongoDB、Couchbase、LevelDB主流 NoSQL 数据库的对比
  4. Firefox扩展开发
  5. Java API ——包装类
  6. C#中的switch case
  7. Linux学习之常用技巧
  8. select下拉菜单反显不可改动,且submit能够提交数据
  9. iOS基础 - 定时器
  10. windows 环境下 dbnamodb 环境搭建与使用
  11. idea配置git,查看git代码&amp;拉取git项目至本地
  12. SVG绘制图形
  13. servlet种下cookie后如何携带cookie继续往下走
  14. 关于hp proliant sl210t服务器远程iLO接口的管理配置
  15. Codeforces Round #239 (Div. 1) 二项式差分
  16. svn提交代码忘写注释怎么办,我想补充上去?
  17. 使用 JMeter 完成常用的压力测试
  18. Redis C#入门
  19. easy UI动态赋值
  20. ubuntu 杂记

热门文章

  1. CSS3复选框动画
  2. 蓝牙技术(BlueTooth)——(一)
  3. (转) Sqoop使用实例讲解
  4. (原)MongoDB在系统中的使用
  5. (三)宇宙大战 Space Battle -- 场景SCENE切换、UserDefaults统计分数、Particle粒子效果
  6. const 常量与 define常量的区别
  7. 总结java操作MySQL 即JDBC的使用
  8. DFS(1)——hdu1518Square
  9. C#-WinForm控制输入框只接受数字输入
  10. PAT 1030 完美数列