您的位置:首页 > 其它

ACM解题总结——HihoCoder1200 (微软笔试题)

2016-08-23 12:34 253 查看
(p.s: 初次尝试用Java做题,感觉还不错。。。

 )

题目来源:

    HihoCoder1200 

题目要求:
    
Little Ho is playing a role-playing game. There are N cities in the game which are numbered from 1 to N. Every time Little Ho moves to
another city his charisma (a kind of character attribute) will increase by 1 point no matter whether the city is visited before. For example if Little Ho’s moving path is 1->2->3->2->3->1 his charisma will increase by 5.
Little Ho wants to maximize his charisma. However moving between cities costs action points. More precisely moving from city i to city j costs him Aij action
points. Little Ho wants to know how many points of charisma he can get by using no more than M action points? He may start at any city.

题目大意:

    题目的要求很简单:给定一个有向图。初始情况下,位于点1。要求在给定最长的移动距离的情况下,计算可以经过的最多的点的个数,点可以重复经过。

解答:
    本题其实考查了一个知识点,就是图的邻接矩阵表示方式的应用。基本的解题思路是:逐渐增加经过的点数,同时查看总的路程是否超出给定的上限,逐渐逼近至最优解即可。以下时具体的解答思路:

·数据结构:

    对于本题,我们采用邻接矩阵对图进行存储。邻接矩阵是一个N×N的矩阵A,其中N为图中点的个数,如果图中从点i到点j存在长度为k的有向边,那么矩阵元素A[i][j]
= k。由于从任意点到它自身是没有边的,因此,这里约定A[i][i] = +∞。下图是一个邻接矩阵表示图的例子:


 
    对于邻接矩阵中的每一个元素A[i][j],我们有另外一种解释方式,即从点i到点j,中间经过0个点的前提下的最短距离。 接下来,我们可以采用一种类似于矩阵相乘的思路,来计算出任意两点间,在中间经过1个点的前提下的最短距离。
    基本思路为:对于点i到点j中间经过1个点的距离,可以枚举每一个节点k作为中间经过的点,此时,i到j的距离为i到k的距离加k到j的距离,因此:记点i到点j经过中间经过一个点的最短距离为A1[i][j],则:
   


 
选取不同的k点,求得点i到点j的最短距离,因此A1[i][j]的最终计算方法是:



计算所有的A1[i][j],就可以得到新的矩阵A1,该矩阵存储了任意两点间在经过一个中间点的前提下的最短距离。对于上面的样例,A1矩阵计算结果如下:



    对于中间经过2个点、3个点 ...... n个点的情况的处理也是类似的,将对应的矩阵分别记为A2,A3,...
An,这里可以得到更一般的结论:
    

 
    这里的乘法不是传统意义上的矩阵相乘,结果矩阵中的每一个元素的值利用上面的公式得出。做一次上面的计算所需的时间是O(N³)。

    此时,我们得到了一个解答本题的基本思路:首先根据输入的图得到矩阵A0,并通过A0得到矩阵A1,A2,
... 然后检查A0, A1, A2,... 中的每一个元素,如果矩阵Ak中存在小于约定最大值的元素Ak[i][j],那么说明存在一条从i点经过k个中间点到点j的路径,并且该路径的长度不超过给定的最值。所以,通过遍历找到使得矩阵Ak中至少包含一个小于给定最值的元素的k值后,k+1就是需要求解的答案。

·计算优化:

    上文中给出了求解本题的基本的思路。但是,由A0得到A1的时间复杂度是O(N³),由A1得到A2的时间复杂度同样是O(N³),因此如果遍历M个矩阵,计算矩阵的时间就是O(M×N³),同时,检查每个矩阵元素的时间复杂度是O(N²),因此,总的是间复杂度是O(M×N³)。这样的计算方式效率很低。
    由于图中的边的长度最小为1,因此,对于给定的路程上限M,通过计算矩阵A1,A2,...
AM一定可以得到最终的结果,即对于给定的路程上限M,不论采用怎样的策略,经过的点的个数一定不会超过M个。
    这里可以采用快速计算幂的思想,预先计算出A1, A2, A4, ... AlogM,然后,对于任意的矩阵Ai,均可以采用A1,
A2, A4, ... AlogM中的某些项得到。
    然后采用二分的思想,首先考察AlogM是否符合,如果符合,将AlogM和AlogM/2合并,检查是否符合,如果符合再合并下一项,如果不符合,则将当前项恢复,继续合并下一项考察。

    当最终遍历至A1时,就得到了符合条件的矩阵Ak对应的最大的k值。此时,计算的复杂度是O(logM×N³),就可以通过了。

输入输出格式:
    输入:


Line 1: N and M, the number of cities and the initial
action points.

Line 2~N+1: An N x N matrix A. Aij is
the action point cost as described above.

For 30% of the data: 2≤N≤5,1≤M≤50,0≤Aij≤10

For 60% of the data: 2≤N≤50,1≤M≤2000,0≤Aij≤10

For 100% of the data: 2≤N≤100,1≤M≤1,000,000,000,0≤Aij≤20

    输出:

         The
maximum points of charisma Little Ho can get.

程序代码:

import java.util.Scanner;

/**
* This is the ACM problem solving program for hihoCoder 1200.
*
* @version 2016-08-13
* @author Zhang Yufei.
*/
public class Main{
// Input data.
private static int N, M;

/**
* Define the matrix to record the graph.
*
* @author Zhang Yufei
*/
private static class Graph {
public int[][] matrix;

public Graph(int N) {
matrix = new int

;

for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
matrix[i][j] = -1;
}
}
}
}

/*
* Define the array to store the graph. dist[s].matrix[i][j] means the
* minimum distance between node i and node j, given that the path between
* those nodes contains s nodes.
*/
private static Graph[] dist;

/**
* This is the main program.
*
* @param args
* command line parameter list.
*/
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

N = scan.nextInt();
M = scan.nextInt();

int count = (int) (Math.log(M) / Math.log(2)) + 1;
dist = new Graph[count];

for (int i = 0; i < count; i++) {
dist[i] = new Graph(N);
}

for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
dist[0].matrix[i][j] = scan.nextInt();
if(i == j) {
dist[0].matrix[i][j] = -1;
}
}
}

scan.close();

for (int i = 1; i < count; i++) {
compute(dist[i - 1], dist[i - 1], dist[i]);
}

int ans = 0;
Graph temp1 = new Graph(N);
Graph temp2 = new Graph(N);
int c;
for (c = count - 1; c >= 0; c--) {
if (check(dist[c])) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
temp1.matrix[i][j] = dist[c].matrix[i][j];
}
}
ans += (int) Math.pow(2, c);
break;
}
}

for (c--; c >= 0; c--) {
compute(temp1, dist[c], temp2);
if (check(temp2)) {
ans += (int) Math.pow(2, c);
Graph t = temp1;
temp1 = temp2;
temp2 = t;
}
}

System.out.println(ans);
}

/**
* This function computes the minimum distance between every pairs of node
* in graph from all paths which contains the (m + n) node.
*
* @param g1
* The matrix1 to compute. It records the minimum distance
* between every pairs of node given that the path contains m
* nodes.
* @param g2
* The another matrix to compute.It records the minimum distance
* between every pairs of node given that the path contains n
* nodes.
* @param result
* The store space of the result matrix, which contains the
* minimum distance between every pairs of node in graph from all
* paths which contains the (m + n) node.
*/
public static void compute(Graph g1, Graph g2, Graph result) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
result.matrix[i][j] = -1;
for (int k = 0; k < N; k++) {
if (g1.matrix[i][k] != -1 && g2.matrix[k][j] != -1) {
int t = g1.matrix[i][k] + g2.matrix[k][j];
if (result.matrix[i][j] == -1 || result.matrix[i][j] > t) {
result.matrix[i][j] = t;
}
}
}
}
}
}

/**
* This function checks if the matrix is legal.
*
* @param g
* The matrix to check.
* @return If the matrix is legal, returns <code>true</code> or returns
* <code>false</code>
*/
public static boolean check(Graph g) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (g.matrix[i][j] != -1 && g.matrix[i][j] <= M) {
return true;
}
}
}

return false;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息