[bzoj2595][斯坦纳树]游览计划

本文深入探讨了斯坦纳树算法,一种用于解决特定点集连接问题的高效算法。通过实例讲解,展示了如何利用斯坦纳树算法来寻找连接点集代价最小的方案。文章详细解释了算法的两种转移方式,包括枚举子树和使用SPFA算法进行转移,以及如何通过动态规划求解最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

在这里插入图片描述
在这里插入图片描述

Input

第一行有两个整数,N和 M,描述方块的数目。 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点;
否则表示控制该方块至少需要的志愿者数目。 相邻的整数用 (若干个) 空格隔开, 行首行末也可能有多余的空格。

Output

由 N + 1行组成。第一行为一个整数,表示你所给出的方案 中安排的志愿者总数目。 接下来 N行,每行M
个字符,描述方案中相应方块的情况: z ‘_’(下划线)表示该方块没有安排志愿者; z
‘o’(小写英文字母o)表示该方块安排了志愿者; z ‘x’(小写英文字母x)表示该方块是一个景点;
注:请注意输出格式要求,如果缺少某一行或者某一行的字符数目和要求不 一致(任何一行中,多余的空格都不允许出现) ,都可能导致该测试点不得分。

Sample Input

4 4

0 1 1 0

2 5 5 1

1 5 5 1

0 1 1 0

Sample Output

6

xoox

___o

___o

xoox

HINT

对于100%的数据,N,M,K≤10,其中K为景点的数目。输入的所有整数均在[0,2^16]的范围内

题解

偷学了一下斯坦纳树
大概就是解决这样一个问题
给定一个集合,要求把这个集合里面的点连起来并且代价最小,可以引入集合外的点
可以设f[x][y][mask]f[x][y][mask]f[x][y][mask]表示位置在(x,y)(x,y)(x,y)的点为根,与给定集合点连接状态为maskmaskmask的最小权
两种转移
f[x][y][mask]=min(f[x][y][submask]+f[x][y][masksubmask]−val[x][y])f[x][y][mask]=min(f[x][y][submask]+f[x][y][mask^submask]-val[x][y])f[x][y][mask]=min(f[x][y][submask]+f[x][y][masksubmask]val[x][y])
就是枚举它的两个大型子树(子集)
另一种就是
f[x][y][mask]=min(f[x′][y′][mask]+val[x][y])f[x][y][mask]=min(f[x'][y'][mask]+val[x][y])f[x][y][mask]=min(f[x][y][mask]+val[x][y])
其中(x′,y′)与(x,y)(x',y')与(x,y)(x,y)(x,y)相连
容易发现第二种可以用spfa转移
第一种直接转移
然后就是个裸的了…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=11;
const int MAXMASK=(1<<10)+5;
const int dx[4]={0,-1,0,1};
const int dy[4]={-1,0,1,0};
int bin[25];
int f[MAXN][MAXN][MAXMASK],n,m,tot;
int val[MAXN][MAXN];
int pt(int x,int y){return (x-1)*m+y;}
bool in(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m;}

queue<pii> li;
bool vis[MAXN][MAXN];
struct pre
{
	int x,y,ok1,ok2;
	pre(){}
	pre(int _x,int _y,int _ok1,int _ok2){x=_x;y=_y;ok1=_ok1;ok2=_ok2;}
}P[MAXN][MAXN][MAXMASK];
void spfa(int S)
{
	while(!li.empty())
	{
		int x=li.front().first,y=li.front().second;
		vis[x][y]=false;li.pop();
		for(int k=0;k<=3;k++)
		{
			int u=x+dx[k],v=y+dy[k];
			if(in(u,v)&&f[u][v][S]>f[x][y][S]+val[u][v])
			{
				f[u][v][S]=f[x][y][S]+val[u][v];
				P[u][v][S]=pre(x,y,0,0);
				if(!vis[u][v])vis[u][v]=true,li.push(mp(u,v));
			}
		}
	}
}

void dfs(int x,int y,int mask)
{
	if(!mask)return ;
	if(val[x][y])val[x][y]=-1;
	if(P[x][y][mask].x)dfs(P[x][y][mask].x,P[x][y][mask].y,mask);
	else 
	{
		dfs(x,y,P[x][y][mask].ok1);
		dfs(x,y,P[x][y][mask].ok2);
	}
}
int main()
{
	bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1;
	n=read();m=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)val[i][j]=read();
	
	memset(f,63,sizeof(f));
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(!val[i][j])
	{
		f[i][j][bin[tot]]=0;
		tot++;
	}
	tot--;
	for(int mask=1;mask<bin[tot+1];mask++)
	{
		for(int x=1;x<=n;x++)
			for(int y=1;y<=m;y++)
			{
				for(int nxt=(mask-1)&mask;nxt;nxt=(nxt-1)&mask)
					if(f[x][y][nxt]+f[x][y][mask^nxt]-val[x][y]<f[x][y][mask])
					{
						f[x][y][mask]=f[x][y][nxt]+f[x][y][mask^nxt]-val[x][y];
						P[x][y][mask]=pre(0,0,nxt,mask^nxt);
					}
				if(f[x][y][mask]<1e9)li.push(mp(x,y)),vis[x][y]=true;
			}
		spfa(mask);
	}
	
	int ans=(1<<31-1);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
		ans=min(ans,f[i][j][bin[tot+1]-1]);
	pr2(ans);bool tf=false;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)if(ans==f[i][j][bin[tot+1]-1])
		{
			dfs(i,j,bin[tot+1]-1);
			tf=true;break;
		}
		if(tf)break;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(!val[i][j])putchar('x');
			else if(val[i][j]==-1)putchar('o');
			else putchar('_');
		}
		puts("");
	}
	return 0;
}
CH341A编程器是一款广泛应用的通用编程设备,尤其在电子工程和嵌入式系统开发领域中,它被用来烧录各种类型的微控制器、存储器和其他IC芯片。这款编程器的最新版本为1.3,它的一个显著特点是增加了对25Q256等32M芯片的支持。 25Q256是一种串行EEPROM(电可擦可编程只读存储器)芯片,通常用于存储程序代码、配置数据或其他非易失性信息。32M在这里指的是存储容量,即该芯片可以存储32兆位(Mbit)的数据,换算成字节数就是4MB。这种大容量的存储器在许多嵌入式系统中都有应用,例如汽车电子、工业控制、消费电子设备等。 CH341A编程器的1.3版更新,意味着它可以与更多的芯片型号兼容,特别是针对32M容量的芯片进行了优化,提高了编程效率和稳定性。26系列芯片通常指的是Microchip公司的25系列SPI(串行外围接口)EEPROM产品线,这些芯片广泛应用于各种需要小体积、低功耗和非易失性存储的应用场景。 全功能版的CH341A编程器不仅支持25Q256,还支持其他大容量芯片,这意味着它具有广泛的兼容性,能够满足不同项目的需求。这包括但不限于微控制器、EPROM、EEPROM、闪存、逻辑门电路等多种类型芯片的编程。 使用CH341A编程器进行编程操作时,首先需要将设备通过USB连接到计算机,然后安装相应的驱动程序和编程软件。在本例中,压缩包中的"CH341A_1.30"很可能是编程软件的安装程序。安装后,用户可以通过软件界面选择需要编程的芯片类型,加载待烧录的固件或数据,然后执行编程操作。编程过程中需要注意的是,确保正确设置芯片的电压、时钟频率等参数,以防止损坏芯片。 CH341A编程器1.3版是面向电子爱好者和专业工程师的一款实用工具,其强大的兼容性和易用性使其在众多编程器中脱颖而出。对于需要处理25Q256等32M芯片的项目,或者26系列芯片的编程工作,CH341A编程器是理想的选择。通过持续的软件更新和升级,它保持了与现代电子技术同步,确保用户能方便地对各种芯片进行编程和调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值