HDU 4085 Peach Blossom Spring 记忆化搜索枚举子集 斯坦纳树

题目链接:点击打开链接

题意:

第一行输入n个点 m条可修建的无向边 k个人

下面给出修建的边和修建该边的花费。

开始时k个人在1-k的每个点上(一个点各一人)

目标:从m条给定边中修建部分边使得花费和最小

让k个人移动到 [n-k+1, n] 后面的k个点上(每个点放一个人)。

思路:

首先就是一道斯坦纳树,还是先求一个dp数组(求解方法:点击打开链接

dp[i][j] 表示以i为根 ,j为8个点中是否在 i 的子树里 时的最小花费。

现在的问题就是如何求答案。

因为一个人到他的目标点这条路径可能和别人的不连通,也就是多个最小生成树,

我们枚举2*k个点哪些点是在一个连通分量里的,

则对于状态x中,表示人的二进制是低k位,表示目标点的是高k位,x中1表示这些点是在一个连通分量里的,

对于这个x的最小花费就是min( dp[ anypoint regard root ][x])

而x必须保证低k位中1的个数 和高k位中1的个数相同(即人数和目标点个数相同)

然后记忆化搜索即可。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
template <class T>
inline bool rd(T &ret) {
    char c; int sgn;
    if (c = getchar(), c == EOF) return 0;
    while (c != '-' && (c<'0' || c>'9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}
template <class T>
inline void pt(T x) {
    if (x <0) {
        putchar('-');
        x = -x;
    }
    if (x>9) pt(x / 10);
    putchar(x % 10 + '0');
}
int Pow(int x, int y) {
    int ans = 1;
    while (y > 0) {
        if ((y & 1) > 0)
            ans *= x;
        y >>= 1;
        x = x * x;
    }
    return ans;
}
using namespace std;
const int inf = 1e8;
const int N = 55;
const int M = 500010;
int n, m, k, ans;
int dis[N][N], dp[N][1 << 10];
int e[10];
int vis[N];
int mem[1 << 10];
int check(int x){
    int a = 0, b = 0;
    for (int i = 0; i < k; i++)
    if ((x&(1 << i))>0)a++;
    for (int i = 0; i < k; i++)
    if ((x&(1 << (i + k)))>0)b++;
    if (a != b)return -1;
    int ans = inf;
    for (int i = 0; i < n; i++)
        ans = min(ans, dp[i][x]);
    return ans;
}
int dfs(int x){
    if (mem[x] != -1)return mem[x];
    int tmp = check(x);
    if (tmp == -1)return mem[x] = inf;
    int ans = tmp;
    for (int i = (x-1)&x; i > 0; i = (i - 1)&x){
        ans = min(ans, dfs(i) + dfs(x - i));
    }
    return mem[x] = ans;
}
void floyd(){
    for (int z = 0; z < n; z++)
    for (int j = 0; j < n; j++)
    for (int i = 0; i < n; i++)
        dis[i][j] = min(dis[i][j], dis[j][z] + dis[z][i]);
}
void input(){
    rd(n); rd(m); rd(k);
    for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)dis[i][j] = (i==j)?0:inf;
    int u, v, d;
    while (m-->0){
        rd(u); rd(v); rd(d);
        u--; v--;
        dis[u][v] = dis[v][u] = min(dis[u][v], d);
    }
    for (int z = 0; z < n; z++)
    for (int j = 0; j < n; j++)
    for (int i = 0; i < n; i++)
        dis[i][j] = min(dis[i][j], dis[j][z] + dis[z][i]);
}
int main(){
    int T; rd(T);
    while (T-->0){
        input();
        floyd();
        for (int i = 0; i < k; i++)e[i] = i; for (int i = 0; i < k; i++)e[i + k] = n - k + i;
        for (int i = 0; i < n; i++)for (int j = 0; j < (1 << (2 * k)); j++)dp[i][j] = inf;
        for (int i = 0; i < n; i++){
            for (int j = 0; j < 2 * k; j++)
                dp[i][1 << j] = dis[i][e[j]];
        }
        for (int i = 1; i < (1 << (2 * k)); i++){
            if (0 == (i&(i - 1)))continue;
            for (int j = 0; j < n; j++){
                for (int sub = i; sub > 0; sub = (sub - 1)&i)
                    dp[j][i] = min(dp[j][i], dp[j][sub] + dp[j][i-sub]);
            }
            for (int j = 0; j < n; j++)vis[j] = 0;
            for (int j = 0; j < n; j++){
                int a = inf, pos = 0;
                for (int z = 0; z < n; z++)
                if (vis[z] == 0 && dp[z][i] <= a)
                    a = dp[pos = z][i];
                vis[pos] = 1;
                for (int z = 0; z < n; z++)
                    dp[pos][i] = min(dp[pos][i], dp[z][i] + dis[z][pos]);
            }
        }
        for (int j = k; j < 2 * k; j++)vis[j] = -1;
        memset(mem, -1, sizeof mem);
        mem[0] = 0;
        ans = dfs((1<<(2*k))-1);
        if (ans == inf)printf("No solution\n");
        else printf("%d\n",ans);
    }
    return 0;
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Scanner;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Queue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class Main {
	int n, m, k, ans;
	int[][] dis = new int[N][N], dp = new int[N][1<<10];
	int[] e = new int[10];
	int[] vis = new int[N], mem = new int[1<<10];
	int check(int x){
		int a = 0, b = 0;
		for (int i = 0; i < k; i++)
		if ((x&(1 << i))>0)a++;
		for (int i = 0; i < k; i++)
		if ((x&(1 << (i + k)))>0)b++;
		if (a != b)return -1;
		int ans = inf;
		for (int i = 0; i < n; i++)
			ans = min(ans, dp[i][x]);
		return ans;
	}
	int dfs(int x){
		if (mem[x] != -1)return mem[x];
		int tmp = check(x);
		if (tmp == -1)return mem[x] = inf;
		int ans = tmp;
		for (int i = (x-1)&x; i > 0; i = (i - 1)&x){
			ans = min(ans, dfs(i) + dfs(x - i));
		}
		return mem[x] = ans;
	}
	void floyd(){
		for(int z = 0; z < n; z++)
			for(int j = 0; j < n; j++)
				for(int i = 0; i < n; i++)
					dis[i][j] = min(dis[i][j], dis[j][z] + dis[z][i]);
	}
	void input() throws Exception{
		for(int i = 0; i < n; i++)for(int j = 0; j < n; j++)dis[i][j] = (i==j)?0:inf;
		while(m-->0){
			int u = Int() - 1, v = Int() - 1, d = Int();
			dis[u][v] = dis[v][u] = min(dis[u][v], d);
		}
	}
	void work() throws Exception{
		int T = Int();
		while(T-->0){
			n = Int(); m = Int(); k = Int();
			input();
			floyd();
			for (int i = 0; i < k; i++)e[i] = i; for (int i = 0; i < k; i++)e[i + k] = n - k + i;
			for (int i = 0; i < n; i++)for (int j = 0; j < (1 << (2 * k)); j++)dp[i][j] = inf;
			for (int i = 0; i < n; i++){
				for (int j = 0; j < 2 * k; j++)
					dp[i][1 << j] = dis[i][e[j]];
			}
			for (int i = 1; i < (1 << (2 * k)); i++){
				if (0 == (i&(i - 1)))continue;
				for (int j = 0; j < n; j++){
					for (int sub = i; sub > 0; sub = (sub - 1)&i)
						dp[j][i] = min(dp[j][i], dp[j][sub] + dp[j][i-sub]);
				}
				for (int j = 0; j < n; j++)vis[j] = 0;
				for (int j = 0; j < n; j++){
					int a = inf, pos = 0;
					for (int z = 0; z < n; z++)
					if (vis[z] == 0 && dp[z][i] <= a)
						a = dp[pos = z][i];
					vis[pos] = 1;
					for (int z = 0; z < n; z++)
						dp[pos][i] = min(dp[pos][i], dp[z][i] + dis[z][pos]);
				}
			}
			for(int i = 1; i < (1<<(2*k)); i++)mem[i] = -1;
			mem[0] = 0;
			ans = dfs((1<<(2*k))-1);
			if(ans == inf)out.println("No solution");
			else out.println(ans);
		}
	}

    public static void main(String[] args) throws Exception{
        Main wo = new Main();
    	in = new BufferedReader(new InputStreamReader(System.in));
    	out = new PrintWriter(System.out);
  //  	in = new BufferedReader(new InputStreamReader(new FileInputStream(new File("input.txt"))));
  //  	out = new PrintWriter(new File("output.txt"));
        wo.work();
        out.close();
    }

	static int N = 55;
	static int M = 2005;
	DecimalFormat df=new DecimalFormat("0.0000");
	static int inf = (int)1e8;
	static long inf64 = (long) 1e18*2;
	static double eps = 1e-8;
	static double Pi = Math.PI;
	static int mod = 1000000009 ;

	private String Next() throws Exception{
    	while (str == null || !str.hasMoreElements())
    	    str = new StringTokenizer(in.readLine());
    	return str.nextToken();
    }
    private int Int() throws Exception{
    	return Integer.parseInt(Next());
    }
    private long Long() throws Exception{
    	return Long.parseLong(Next());
    }
    private double Double() throws Exception{
    	return Double.parseDouble(Next());
    }
    StringTokenizer str;
    static Scanner cin = new Scanner(System.in);
    static BufferedReader in;
    static PrintWriter out;
  /*
	class Edge{
		int from, to, dis, nex;
		Edge(){}
		Edge(int from, int to, int dis, int nex){
			this.from = from;
			this.to = to;
			this.dis = dis;
			this.nex = nex;
		}
	}
	Edge[] edge = new Edge[M<<1];
	int[] head = new int[N];
	int edgenum;
	void init_edge(){for(int i = 0; i < N; i++)head[i] = -1; edgenum = 0;}
	void add(int u, int v, int dis){
		edge[edgenum] = new Edge(u, v, dis, head[u]);
		head[u] = edgenum++;
	}/**/
	int upper_bound(int[] A, int l, int r, int val) {// upper_bound(A+l,A+r,val)-A;
		int pos = r;
		r--;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (A[mid] <= val) {
				l = mid + 1;
			} else {
				pos = mid;
				r = mid - 1;
			}
		}
		return pos;
	}

	int Pow(int x, int y) {
		int ans = 1;
		while (y > 0) {
			if ((y & 1) > 0)
				ans *= x;
			y >>= 1;
			x = x * x;
		}
		return ans;
	}
	double Pow(double x, int y) {
		double ans = 1;
		while (y > 0) {
			if ((y & 1) > 0)
				ans *= x;
			y >>= 1;
			x = x * x;
		}
		return ans;
	}
	int Pow_Mod(int x, int y, int mod) {
		int ans = 1;
		while (y > 0) {
			if ((y & 1) > 0)
				ans *= x;
			ans %= mod;
			y >>= 1;
			x = x * x;
			x %= mod;
		}
		return ans;
	}
	long Pow(long x, long y) {
		long ans = 1;
		while (y > 0) {
			if ((y & 1) > 0)
				ans *= x;
			y >>= 1;
			x = x * x;
		}
		return ans;
	}
	long Pow_Mod(long x, long y, long mod) {
		long ans = 1;
		while (y > 0) {
			if ((y & 1) > 0)
				ans *= x;
			ans %= mod;
			y >>= 1;
			x = x * x;
			x %= mod;
		}
		return ans;
	}

	int gcd(int x, int y){
		if(x>y){int tmp = x; x = y; y = tmp;}
		while(x>0){
			y %= x;
			int tmp = x; x = y; y = tmp;
		}
		return y;
	}
	int max(int x, int y) {
		return x > y ? x : y;
	}

	int min(int x, int y) {
		return x < y ? x : y;
	}

	double max(double x, double y) {
		return x > y ? x : y;
	}

	double min(double x, double y) {
		return x < y ? x : y;
	}

	long max(long x, long y) {
		return x > y ? x : y;
	}

	long min(long x, long y) {
		return x < y ? x : y;
	}

	int abs(int x) {
		return x > 0 ? x : -x;
	}

	double abs(double x) {
		return x > 0 ? x : -x;
	}

	long abs(long x) {
		return x > 0 ? x : -x;
	}

	boolean zero(double x) {
		return abs(x) < eps;
	}
	double sin(double x){return Math.sin(x);}
	double cos(double x){return Math.cos(x);}
	double tan(double x){return Math.tan(x);}
	double sqrt(double x){return Math.sqrt(x);}
}
时间: 2024-10-06 07:54:33

HDU 4085 Peach Blossom Spring 记忆化搜索枚举子集 斯坦纳树的相关文章

hdu 4085 Peach Blossom Spring

Peach Blossom Spring 题意:有n个城市,m条路,[1,k]的城市有居民, [n-k+1, n]的城市有庇护所, 现在要修路, 使得每一座城市的居民都可以到达一个庇护所, 并且一个庇护所只能容纳一个城市的居民, 现在求所有城市的居民都能到达庇护所的最小花费. 题解:斯坦纳树跑出花费. d[i][state] = cost, i代表的是第i个城市, state代表的是联通的状态, val代表的是花费. 先跑出所有的d[i][state]的花费.然后由于不需要是联通图, 再最后跑出

HDU 4085 Peach Blossom Spring 斯坦纳树 状态压缩DP+SPFA

状态压缩dp+spfa解斯坦纳树 枚举子树的形态 dp[i][j] = min(dp[i][j], dp[i][k]+dp[i][l]) 其中k和l是对j的一个划分 按照边进行松弛 dp[i][j] = min(dp[i][j], dp[i'][j]+w[i][j])其中i和i'之间有边相连 #include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn

HDU 4597 Play Game (记忆化搜索)

题意:有两堆n张的卡片,每张卡片有一个得分,Alice和Bob轮流在两堆卡片的两端取卡片 问Alice先手,取得分数最多为多少: #include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #include <queue> #include <math.h> #define M 50 #define LL long long using

hdu 1501 Zipper (dfs+记忆化搜索)

Zipper Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6491    Accepted Submission(s): 2341 Problem Description Given three strings, you are to determine whether the third string can be formed

HDU 5024 (广州网络赛) Wang Xifeng&#39;s Little Plot 记忆化搜索+枚举

Problem Description <Dream of the Red Chamber>(also <The Story of the Stone>) is one of the Four Great Classical Novels of Chinese literature, and it is commonly regarded as the best one. This novel was created in Qing Dynasty, by Cao Xueqin.

HDU 1078 FatMouse and Cheese(记忆化搜索DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1078 题目大意:一个n*n的图,每个点都有奶酪,老鼠从(0,0)开始走,每次最多只能走k步就要停下来,停下的这个位置的奶酪数只能比上一个停留的位置大,并获取其奶酪,每次只能水平或垂直走,问最多能得到的奶酪. 解题思路:记忆化搜索,这方面还是写的太少,还要看别人才会,这个就当个例子参考吧. 1 #include<cstdio> 2 #include<cstring> 3 #include

hdu 1142(迪杰斯特拉+记忆化搜索)

A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 7330    Accepted Submission(s): 2687 Problem Description Jimmy experiences a lot of stress at work these days, especiall

HDU 1428 漫步校园 (BFS + 记忆化搜索)

漫步校园 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3360    Accepted Submission(s): 1009 Problem Description LL最近沉迷于AC不能自拔,每天寝室.机房两点一线.由于长时间坐在电脑边,缺乏运动.他决定充分利用每次从寝室到机房的时间,在校园里散散步.整个HDU校园呈方形布局,可

hdu 1176免费馅饼(记忆化搜索)

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1176 题意不解释了 简单的记忆化搜索可以拿来练练手,注意要从pos = 5 开始搜索 #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <cmath> using namespace std; const int M = 1e5