• 【BZOJ2595_洛谷4294】[WC2008]游览计划(斯坦纳树_状压DP)


    上个月写的题qwq……突然想写篇博客

    题目:

    洛谷4294

    分析:

    斯坦纳树模板题。

    简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点),且边权(或点权)之和最小。很明显,这样最终形成的是一棵树。

    通常,斯坦纳树问题规模都比较小。考虑状压DP。用(dp[u][S])表示让点(u)与集合(S)中所有关键点连通的最小花费。有如下两种转移:

    第一,把两条到(u)的路径拼在一起,减去重合点(u)的点权,即((w_u)表示点(u)的点权,(S')表示(S)的一个真非空子集,(S-S')表示以(S')相对于(S)的补集,下同):

    [dp[u][S]=min(dp[u][S']+dp[u][S-S']-w_u) ]

    第二,延伸一条路径,即((v)(u)之间存在一条边):

    [dp[u][S]=min(dp[v][S]+w_u) ]

    第二种存在循环更新的问题。但是它长得很像最短路,于是大力跑最短路算法即可。

    注意更新顺序,要从小到大枚举集合(S),先更新第一种再更新第二种。

    代码:

    把上面的点(u)换成坐标就好了……

    dp的时候记一下从哪个状态转移来的。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cctype>
    #include <queue>
    #include <functional>
    using namespace std;
    #define _ 0
    
    namespace zyt
    {
    	template<typename T>
    	inline void read(T &x)
    	{
    		char c;
    		bool f = false;
    		x = 0;
    		do
    			c = getchar();
    		while (c != '-' && !isdigit(c));
    		if (c == '-')
    			f = true, c = getchar();
    		do
    			x = x * 10 + c - '0', c = getchar();
    		while (isdigit(c));
    		if (f)
    			x = -x;
    	}
    	template<typename T>
    	inline void write(T x)
    	{
    		static char buf[20];
    		char *pos = buf;
    		if (x < 0)
    			putchar('-'), x = -x;
    		do
    			*pos++ = x % 10 + '0';
    		while (x /= 10);
    		while (pos > buf)
    			putchar(*--pos);
    	}
    	const int N = 10, ST = 1 << N, INF = 0x3f3f3f3f;
    	struct _pre
    	{
    		int x, y, st;
    		_pre(const int _x = -1, const int _y = -1, const int _st = -1)
    			: x(_x), y(_y), st(_st) {}
    	}pre[N][N][ST];
    	struct point
    	{
    		int x, y;
    		point(const int _x = 0, const int _y = 0)
    			: x(_x), y(_y) {}
    		bool operator < (const point &b) const
    		{
    			return x == b.x ? y < b.y : x < b.x;
    		}
    	};
    	int f[N][N][ST], arr[N][N], n, m, k;
    	const int dx[] = {0, 0, 1, -1};
    	const int dy[] = {1, -1, 0, 0};
    	void Dijkstra(const int s)
    	{
    		typedef pair<int, point> pip;
    		static priority_queue<pip, vector<pip>, greater<pip> > q;
    		static bool vis[N][N];
    		while (!q.empty())
    			q.pop();
    		for (int i = 0; i < n; i++)
    			for (int j = 0; j < m; j++)
    			{
    				q.push(make_pair(f[i][j][s], point(i, j)));
    				vis[i][j] = false;
    			}
    		while (!q.empty())
    		{
    			point u = q.top().second;
    			q.pop();
    			vis[u.x][u.y] = true;
    			for (int i = 0; i < 4; i++)
    			{
    				point v = point(u.x + dx[i], u.y + dy[i]);
    				if (v.x < 0 || v.x >= n || v.y < 0 || v.y >= m || vis[v.x][v.y])
    					continue;
    				if (f[v.x][v.y][s] > f[u.x][u.y][s] + arr[v.x][v.y])
    				{
    					f[v.x][v.y][s] = f[u.x][u.y][s] + arr[v.x][v.y];
    					pre[v.x][v.y][s] = _pre(u.x, u.y, s);
    					q.push(make_pair(f[v.x][v.y][s], v));
    				}
    			}
    		}
    	}
    	bool mark[N][N];
    	void dfs(const _pre p)
    	{
    		mark[p.x][p.y] = true;
    		_pre nxt = pre[p.x][p.y][p.st];
    		if (nxt.x == -1 && nxt.y == -1 && nxt.st == -1)
    			return;
    		dfs(nxt);
    		if (nxt.st != p.st)
    		{
    			nxt.st ^= p.st;
    			dfs(nxt);
    		}
    	}
    	int work()
    	{
    		read(n), read(m);
    		for (int i = 0; i < n; i++)
    		{
    			memset(f[i], INF, sizeof(int[m][ST]));
    			for (int j = 0; j < m; j++)
    			{
    				read(arr[i][j]);
    				if (!arr[i][j])
    					f[i][j][1 << (k++)] = 0;
    			}
    		}
    		for (int i = 0; i < (1 << k); i++)
    		{
    			for (int j = (i - 1) & i; j; j = (j - 1) & i)
    				for (int x = 0; x < n; x++)
    					for (int y = 0; y < m; y++)
    						if (f[x][y][i] > f[x][y][j] + f[x][y][i ^ j] - arr[x][y])
    						{
    							f[x][y][i] = f[x][y][j] + f[x][y][i ^ j] - arr[x][y];
    							pre[x][y][i] = _pre(x, y, j);
    						}
    			Dijkstra(i);
    		}
    		int ans = INF;
    		_pre pans;
    		for (int i = 0; i < n; i++)
    			for (int j = 0; j < m; j++)
    				if (ans > f[i][j][(1 << k) - 1])
    				{
    					ans = f[i][j][(1 << k) - 1];
    					pans = _pre(i, j, (1 << k) - 1);
    				}
    		write(ans), putchar('
    ');
    		dfs(pans);
    		for (int i = 0; i < n; i++)
    		{
    			for (int j = 0; j < m; j++)
    				if (!arr[i][j])
    					putchar('x');
    				else if (mark[i][j])
    					putchar('o');
    				else
    					putchar('_');
    			putchar('
    ');
    		}
    		return (0^_^0);
    	}	
    }
    int main()	
    {
    	return zyt::work();
    }
    
  • 相关阅读:
    GMA Round 1 数列求单项
    GMA Round 1 双曲线与面积
    多线程环境中安全使用集合API(含代码)
    使用synchronized获取互斥锁的几点说明
    ThreadPoolExecutor线程池
    线程状态转换
    volatile关键字
    守护线程与线程阻塞的四种情况
    线程挂起,恢复与终止
    线程中断
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/10247966.html
Copyright © 2020-2023  润新知