BZOJ 3205 [Apio2013]机器人 ——斯坦纳树

腊鸡题目,实在卡不过去。

(改了一下午)

就是裸的斯坦纳树的题目,一方面合并子集,另一方面SPFA迭代求解。

优化了许多地方,甚至基数排序都写了。

还是T到死,不打算改了,就这样吧

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define inf 0x3f3f3f3f
#define maxn 502
#define ll long long
#define mp make_pair

short n,r,c,a[maxn][maxn];
int dp[maxn][maxn][10][10];
char s[maxn][maxn];
short mov[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
short go[maxn][maxn][4][2];

void Readin()
{
	memset(dp,0x3f,sizeof dp);
	cin>>n>>c>>r;
	F(i,1,r)
	{
		scanf("%s",s[i]+1);
		F(j,1,c)
		{
			switch(s[i][j])
			{
				case ‘.‘:a[i][j]=0;break;
				case ‘x‘:a[i][j]=-1;break;
				case ‘A‘:a[i][j]=10;break;
				case ‘C‘:a[i][j]=11;break;
				default: a[i][j]=s[i][j]-‘0‘;dp[i][j][a[i][j]][a[i][j]]=0;break;
			}
		}
	}
	F(i,0,r) a[i][0]=a[i][c+1]=-1;F(i,0,c) a[0][i]=a[r+1][i]=-1; a[r+1][c+1]=-1;
}

int vis[maxn][maxn][4],idx;

void dfs(int nx,int ny,int k)
{
	int tmp=k;
	if (vis[nx][ny][k]==idx)
	{
		go[nx][ny][k][0]=-1;
		go[nx][ny][k][1]=-1;
		return;
	}
	vis[nx][ny][k]=idx;
	if (go[nx][ny][k][0]!=0) return;
	switch(a[nx][ny])
	{
		case 10:tmp=(tmp+3)%4;break;
		case 11:tmp=(tmp+1)%4;break;
	}
	if (a[nx+mov[tmp][0]][ny+mov[tmp][1]]==-1)
	{
		go[nx][ny][k][0]=nx;
		go[nx][ny][k][1]=ny;
		return;
	}
	int tx=nx+mov[tmp][0],ty=ny+mov[tmp][1];
	if (!go[tx][ty][tmp][0]) dfs(tx,ty,tmp);
	go[nx][ny][k][0]=go[tx][ty][tmp][0];
	go[nx][ny][k][1]=go[tx][ty][tmp][1];
}

void init()
{
	F(i,1,r) F(j,1,c)
	F(k,0,3){
		int nx=i,ny=j,flag=1;++idx;
		dfs(nx,ny,k);
	}
}

struct Statement{short x,y,l,r; int v;}sta[maxn*maxn],res[maxn*maxn];
queue <Statement> q,d;
int top=0;

bool Com(Statement a, Statement b)
{return a.v<b.v;}

int cnt[200005],ctop=0;

void Radix_Sort()
{
	memset(cnt,0,sizeof cnt);
	F(i,1,top)
	{
		if (sta[i].v>=200000) continue;
		cnt[sta[i].v]++,ctop=max(ctop,sta[i].v);
	}
	F(i,1,ctop) cnt[i]+=cnt[i-1]; int tmp=cnt[ctop];
	F(i,1,top)
	{
		if (sta[i].v>=200000) continue;
		res[cnt[sta[i].v]--]=sta[i];
	}
	F(i,1,tmp) sta[i]=res[i];
	memcpy(sta,res,(top+1)*sizeof(Statement));
}

void SPFA()
{
//	sort(sta+1,sta+top+1,Com);
	Radix_Sort();
	F(i,1,top) q.push(sta[i]);
	while (!q.empty()||!d.empty())
	{
		int nx,ny,l,r,v;
		if (d.empty()||(!d.empty()&&!q.empty()&&q.front().v<=d.front().v))
		{
			Statement now=q.front(); q.pop();
			nx=now.x,ny=now.y,l=now.l,r=now.r,v=now.v;
		}
		else
		{
			Statement now=d.front(); d.pop();
			nx=now.x,ny=now.y,l=now.l,r=now.r,v=now.v;
		}
		if (dp[nx][ny][l][r]<v) continue;
		for (int k=0;k<4;++k)
			if (go[nx][ny][k][0]!=-1)
			{
				int tx=go[nx][ny][k][0],ty=go[nx][ny][k][1];
				if (dp[tx][ty][l][r]>dp[nx][ny][l][r]+1)
				{
					dp[tx][ty][l][r]=dp[nx][ny][l][r]+1;
					Statement now;
					now.x=tx;now.y=ty;now.l=l;now.r=r;now.v=dp[tx][ty][l][r];
					d.push(now);
				}
			}
	}
}
void DP()
{
	F(i,1,r) F(j,1,c) if (a[i][j]>=1&&a[i][j]<=n)
	{++top;sta[top].x=i;sta[top].y=j;sta[top].l=a[i][j];sta[top].r=a[i][j];sta[top].v=dp[i][j][a[i][j]][a[i][j]];}
	SPFA();
	F(len,2,n)
	{
		F(i,1,n-len+1)
		{
			top=0;
			int j=i+len-1;
			F(x,1,r) F(y,1,c)
			{
				F(z,i,j-1) dp[x][y][i][j]=min(dp[x][y][i][z]+dp[x][y][z+1][j],dp[x][y][i][j]);
				if (dp[x][y][i][j]<inf)
				{
					Statement now;
					now.x=x;now.y=y;now.l=i;now.r=j;now.v=dp[x][y][i][j];
					sta[++top]=now;
				}
			}
			SPFA();
		}
	}
	int ans=inf;
	F(i,1,r) F(j,1,c) ans=min(ans,dp[i][j][1][n]);
	printf("%d\n",ans==inf?-1:ans);
}

int main()
{
	Readin();
	init();
	DP();
}

  

时间: 2024-12-22 16:01:59

BZOJ 3205 [Apio2013]机器人 ——斯坦纳树的相关文章

BZOJ 3205 Apio2013 机器人 斯坦纳树

题目大意:给定一张地图,一些地方有障碍物,有k<=9个机器人,可以一推到底,遇到转向器会转向,两个编号相邻的机器人可以合并,求最少推多少次可以全部合并 令f[l][r][i][j]表示在点(i,j)将编号在[l,r]区间内的机器人全部合并的最小推动次数 则有动规方程组: f[l][r][i][j]=min{f[l][r][_i][_j]+1} ( (_i,_j)->(i,j) ) f[l][r][i][j]=min(f[l][temp][i][j]+f[temp+1][r][i][j]) (l

[APIO2013]机器人(斯坦纳树)

题目描述 VRI(Voltron 机器人学会)的工程师建造了 n 个机器人.任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人. 我们把机器人用 1 至 n 编号(n ≤ 9).如果两个机器人的编号是连续的,那 么它们是兼容的,可以合并成一个复合机器人.最初这 n 个机器人各自都只有唯 一的编号.而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号, 分别是构成它的所有机器人中最小和最大的编号. 例如,2 号机器人只可以与 1 号或 3 号机器人合并.若 2 号机器人与 3 号

BZOJ 2595 [Wc2008]游览计划 ——斯坦纳树

[题目分析] 斯坦纳树=子集DP+SPFA? 用来学习斯坦纳树的模板. 大概就是用二进制来表示树包含的点,然后用跟几点表示树的形态. 更新分为两种,一种是合并两个子集,一种是换根,换根用SPFA迭代即可. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include

【BZOJ 2595】2595: [Wc2008]游览计划 (状压DP+spfa,斯坦纳树?)

2595: [Wc2008]游览计划 Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 1572  Solved: 739 Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Output 由 N

BZOJ 2595: [Wc2008]游览计划 [DP 状压 斯坦纳树 spfa]【学习笔记】

传送门 题意:略 论文 <SPFA算法的优化及应用> http://www.cnblogs.com/lazycal/p/bzoj-2595.html 本题的核心就是求斯坦纳树: Steiner Tree: Given an undirected graph with non-negative edge weights and a subset of vertices, usually referred to as terminals, the Steiner tree problem in g

BZOJ 2595 Wc2008 游览计划 斯坦纳树

题目大意:给定一个矩阵,有一些关键点,每个格子有权值,选择一些格子使所有关键点连通,求最小权值和 传说中的斯坦纳树- - 感觉不是很难理解的样子 枚举连通的状态,对于每个状态先对每个位置枚举子集进行合并,然后对这个状态的分层图进行SPFA 看了几分代码还是ZKY写的比较简洁- - 此外就是终于能通过操作符重载访问结构体里的三维数组了- - 我真是太丧病了233 #include <cstdio> #include <cstring> #include <iostream>

FJoi2017 1月20日模拟赛 直线斯坦纳树(暴力+最小生成树+骗分+人工构造+随机乱搞)

[题目描述] 给定二维平面上n个整点,求该图的一个直线斯坦纳树,使得树的边长度总和尽量小. 直线斯坦纳树:使所有给定的点连通的树,所有边必须平行于坐标轴,允许在给定点外增加额外的中间节点. 如下图所示为两种直线斯坦纳树的生成方案,蓝色点为给定的点,红色点为中间节点. [输入格式] 第一行一个整数n,表示点数. 以下n行,每行两个整数表示点的x,y坐标. [输出格式] 第一行一个整数m,表示中间节点的个数. 必须满足m <= 10 * n 以下m行,每行2个整数表示中间节点的坐标. 以下n+m-1

HDU 4085 斯坦纳树模板题

Dig The Wells Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 971    Accepted Submission(s): 416 Problem Description You may all know the famous story "Three monks". Recently they find som

【Foreign】修路 [斯坦纳树]

修路 Time Limit: 20 Sec  Memory Limit: 256 MB Description Input Output 仅一行一个整数表示答案. Sample Input 5 5 2 1 3 4 3 5 2 2 3 1 3 4 4 2 4 3 Sample Output 9 HINT Main idea 给定若干对点,选择若干边,询问满足每对点都连通的最小代价. Source 发现 d 非常小,所以我们显然可以使用斯坦纳树来求解. 斯坦纳树是用来解决这种问题的:给定若干关键点,