Java implementa el entrenamiento del algoritmo Blue Bridge Cup. Recuerde el modo A La (violento)

Prueba Algoritmo Entrenamiento Recuerde el modo A La

Descripción del problema
  Hugh Samston dirige un servicio de catering que ofrece postres para los participantes de las finales mundiales del ICPC de este año. Proporcionará pasteles con helado encima. Para satisfacer diferentes necesidades, preparó muchas rebanadas diferentes de pastel y helado.
  Hugh espera servir postres en forma de una rebanada de helado encima de una rebanada de pastel. Sin embargo, como empresario, espera ganar la mayor cantidad de dinero posible. Él conoce el precio de diferentes tipos de pasteles y combinaciones de helados, y también sabe que esos helados y esos pasteles no se pueden combinar.
  Hugh quiere determinar el rango de ganancias que puede obtener en función del número de cada pedazo de pastel y helado, y las diferentes combinaciones mencionadas anteriormente.
Formato de entrada El formato
  en el que se debe satisfacer la entrada de datos de prueba.
  La primera línea de la entrada contiene dos enteros P e I, que representan los tipos de pasteles y helados.
  La siguiente línea contiene enteros P, que indican el número de sectores de cada tipo.
  La siguiente línea contiene 1 entero, que indica el número de helados de cada tipo.
  En la siguiente línea P, cada línea contiene un número real I, que representa el resultado de cada tipo de combinación de rebanada de pastel y helado.
  Si este pedazo de pastel y este helado se pueden combinar, es un número real (0,10), lo que representa el beneficio de esta combinación.
  De lo contrario, es -1, lo que indica que los dos no se pueden combinar.
Formato de
  salida Salida de una línea y salida del rango de beneficio en forma de "(retorno mínimo) a (retorno máximo)".

Tenga en cuenta: Todos los pasteles y helados deben ser usados.
Entrada de muestra
2 3
40 50
27 30 33
1.11 1.27 0.70
-1 2 0.34
Salida de muestra
91.70 a 105.87
Escala de datos y convención
  0 <P, I <= 50, el número de cada tipo de pastel o helado no excede de 100.

PD:

这个题给我晕坏了,我的天,这半天就肝了这么几道题,躺了,浑身晕

 
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;

public class Main {
	static int[] PArray, IArray, pre, dis;
	
	/*分别用来保存P和I的数量,pre和dis用作SPFA算法中保存前节点和点到源点的距离*/
	
	static int[][] Weight;
	
	/* 保存权重 */
	
	static List<List<Integer>> index;
	
	/* index[i]表示所有从i出发的边的集合 */
	
	static int minCost = 0, maxCost = 0, P, I,count = -1;
	
	static List<Edge> E;
	
	/* 保存图中所有边 */
	
	public static void main(String[] args) 
	{
		Scanner sc = new Scanner(System.in);
		P = sc.nextInt();
		I = sc.nextInt();
		PArray = new int[P];
		IArray = new int[I];
		pre = new int[P + I + 2];
		dis = new int[P + I + 2];
		Weight = new int[P + I + 2][P + I + 2];
		index = new ArrayList<List<Integer>>();
		E = new ArrayList<Edge>();
		sc.nextLine();
		
		for (int i = 0; i < P + I + 2; i++)
		{
			index.add(new ArrayList<Integer>());
		}
		for (int i = 0; i < P; i++)
		{
			PArray[i] = sc.nextInt();
		}
		sc.nextLine();
		for (int i = 0; i < I; i++)
		{
			IArray[i] = sc.nextInt();
		}
		sc.nextLine();
		for (int i = 1; i <= P; i++)
		{
			for (int j = P + 1; j <= I + P; j++)
			{
				BigDecimal in = new BigDecimal(Double.toString(sc.nextDouble()));
				in = in.multiply(new BigDecimal(100));
				String toStr = in.toString();
				int num = Integer.parseInt(toStr.substring(0, toStr.indexOf('.')));
				Weight[i][j] = num;
				if (num != -100)
				{
					add(i, j, Math.min(PArray[i - 1], IArray[j - P - 1]), 0, -num);
					add(j, i, 0, 0, num);
					/* 同时构造边和反向边,正向边的权重先取反,来求最大花费 */
				}
			}
			sc.nextLine();
		}
		for (int i = 1; i <= P; i++)
		{
			add(0, i, PArray[i - 1], 0, 0);
			add(i, 0, 0, 0, 0);
			/* 构造超级源点的边以及反向边 */
		}
		
		for (int i = P + 1; i <= P + I; i++)
		{
			add(i, P + I + 1, IArray[i - P - 1], 0, 0);
			add(P + I + 1, i, 0, 0, 0);
			/* 构造超级汇点的边以及反向边 */
		}
		MCMF(0, P + I + 1);
		maxCost = -minCost;
		E.clear();
		minCost = 0;
		
		/* 重新按照weight数组保存的权重值构造Edge集合,来计算最小收益
		 * 事先已经构造好index集合,这次可以直接向E中add
		 */
		for (int i = 1; i <= P; i++)
		{
			for (int j = P + 1; j <= I + P; j++)
			{
				if (Weight[i][j] != -100)
				{
					E.add(new Edge(i, j, Math.min(PArray[i - 1], IArray[j - P - 1]), 0, Weight[i][j]));
					E.add(new Edge(j, i, 0, 0, -Weight[i][j]));
				}
			}
		}
		for (int i = 1; i <= P; i++)
		{
			E.add(new Edge(0, i, PArray[i - 1], 0, 0));
			E.add(new Edge(i, 0, 0, 0, 0));
		}
		
		for (int i = P + 1; i <= P + I; i++)
		{
			E.add(new Edge(i, P + I + 1, IArray[i - P - 1], 0, 0));
			E.add(new Edge(P + I + 1, i, 0, 0, 0));
		}
		MCMF(0, I + P + 1);
		System.out.format("%.2f to %.2f", minCost / 100.0, maxCost / 100.0);
	}
	
	public static void add(int from, int to, int cap, int flow, int weight)
	{
		/* 构造并保存边时,同时填写index */
		E.add(new Edge(from, to, cap, flow, weight));
		count++;
		index.get(from).add(count);
	}
	
	public static Edge getByNum(int from, int to, List<Edge> E)
	{
		/* 通过起点和终点确定边 */
		for (int i : index.get(from))
		{
			if (E.get(i).to == to)
			{
				return E.get(i);
			}
		}
		return null;
	}
	
	public static void MCMF(int s, int n)
	{
		while (SPFA(s, n))
		{
			int now = n, minFlow = Integer.MAX_VALUE;
			while (now != s)
			{
				/* 算出增广路径上每条边上能允许多出的最大流 */
				minFlow = Math.min(minFlow, getByNum(pre[now], now, E).cap);
				now = pre[now];
			}
			now = n;
			minCost += minFlow * dis[n];
			while (now != s)
			{
				/* 增广路径上每条边的容量减少minFlow, 反向路径则增加minFlow */
				getByNum(pre[now], now, E).cap -= minFlow;
				getByNum(now, pre[now], E).cap += minFlow;
				now = pre[now];
			}
		}
	}
	
	public static boolean SPFA(int s, int n)
	{
		/* 寻找s-n增广路径, 并用pre保存这条路径上每个节点的前节点 */
		Queue<Integer> Q = new LinkedList<Integer>();
		for (int i = 0; i < dis.length; i++)
		{
			dis[i] = Integer.MAX_VALUE;
			pre[i] = -1;
		}
		int[] count = new int[102];
		Q.add(s);
		dis[s] = 0;
		pre[s] = -1;
		count[s]++;
		while (!Q.isEmpty())
		{
			int from = Q.poll();
			for (int i : index.get(from))
			{
				if (E.get(i).cap > 0)
				{
					Edge e = E.get(i);
					int to = e.to;
					if (dis[to] > dis[from] + e.weight)
					{
						/* relax操作 */
						dis[to] = dis[from] + e.weight;
						pre[to] = from;
						if (!Q.contains(to))
						{
							Q.add(to);
							count[to]++;
							if (count[to] > n)
							{
								return false;
							}
						}
					}
				}
			}
		}
		return !(pre[n] == -1);
		/* 单纯写成 return true 则在pre数组全是-1的情况下也会return true */
	}

}

class Edge
{
	int from, to, cap, flow, weight;
	
	public Edge(int f, int t, int c, int fl, int w)
	{
		this.from = f;
		this.to = t;
		this.cap = c;
		this.flow = fl;
		this.weight = w;
	}
}

1794 artículos originales publicados · 30,000 Me gusta + · 4.49 millones de visitas

Supongo que te gusta

Origin blog.csdn.net/a1439775520/article/details/105488935
Recomendado
Clasificación