首页 > 试题广场 >

友好城市

[编程题]友好城市
  • 热度指数:856 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
某国家一共有 n 座城市,有的城市与城市与城市之间有路相连,所有的路都是无向的, n 个城市可以通过道理相互达到,其中 2k 座城市是重要城市,国王希望把这 2k 座城市两两配对,形成 k 对友好城市。
友好城市之间常常需要进行交流,因此国王希望友好城市之间的距离不要太长,于是他定义两个城市 u,v 配对的代价为 u,v 之间最短路的长度,2k 座城市配对的总代价为 k 对城市配对的代价和。
国王想知道配对的最小代价。
数据范围:

输入描述:
第一行输入一个数 n ,表示城市个数。
接下来输入一个 n*n 的邻接矩阵 A , A[i][j] 表示城市 i 和 城市 j 之间边的长度,如果 A[i][j] = -1,表示 i 和 j 之间没有直接相连的边,输入保证 A[i][j] = A[j][i] ,A[i][i] = -1 ,且 n 个城市相互连通。
最后输入一个数 k ,接下来一行 2k 个数,这 2k 个重要城市的编号


输出描述:
输出一个数,最小匹配代价
示例1

输入

4
-1 2 -1 10
2 -1 3 -1
-1 3 -1 1
10 -1 1 -1
2
1 2 3 4

输出

3
/**
floyd完后,需要枚举这2k个点的k种边的组合。
对每条边,只需要固定起点,枚举终点即可。
因为起点和终点互换后还是相同的边。
注释部分掉的部分写的比较啰嗦,因为对全排列不太熟悉。

**/

/**
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    static int[][] A;
    private static int[] cities;
    private static int result = Integer.MAX_VALUE;
    private static int k;
    static boolean[] vis;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        A = new int[n][n];
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                A[i][j] = in.nextInt();
                if(A[i][j] == -1)A[i][j] = 0x3f3f3f3f;
            }
        }
        for(int k=0;k<n;k++){
            for(int i=0;i<n;i++){
                for(int j=0;j<n;j++){
                    A[i][j] = Math.min(A[i][j],A[i][k]+A[k][j]);
                }
            }
        }
        k = in.nextInt();
        cities = new int[2*k];
        for(int i=0;i<2*k;i++)cities[i] = in.nextInt()-1;
        vis = new boolean[16];
        dfs(0,new ArrayList<>());
        System.out.println(result);

    }

 
    //枚举第k条边时,所选的边集为t
  
    static void dfs(int i,ArrayList<Integer> t){
        if(i==k){
            int cur = 0;
            for(int s=0;s<2*k;s+=2){//计算答案
                cur+=A[t.get(s)][t.get(s+1)];
            }
            result = Math.min(cur,result);
            return;
        }
        int s = 0; //找到一个没使用过的作为起点
        for(;s<2*k;s++){
            if(!vis[s])break;//边的起点
        }
        vis[s]=true;
        t.add(cities[s]);
        for(int j=0;j<2*k;j++){//枚举边的终点
            if(vis[j])continue;
            vis[j] = true;
            t.add(cities[j]);
            dfs(i+1,t);//枚举下一条边。
            vis[j]=false;
            t.remove(t.size()-1);
        }
        vis[s] = false;
        t.remove(t.size()-1);
    }
}

**/

import java.util.*;
import java.io.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    static int[][] A;
    private static int[] cities;
    private static int result = Integer.MAX_VALUE;
    private static int k;
    static int vis;
    public static void main(String[] args)throws IOException {
       BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
       int n = Integer.parseInt(br.readLine());
       String[] strs;
        A = new int[n][n];
        for (int i = 0; i < n; i++) {
            strs = br.readLine().split(" ");
            for (int j = 0; j < n; j++) {
                A[i][j]=Integer.parseInt(strs[j]);
                if (A[i][j] == -1)A[i][j] = 0x3f3f3f3f;
            }
        }
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    A[i][j] = Math.min(A[i][j], A[i][k] + A[k][j]);
                }
            }
        }
        k = Integer.parseInt(br.readLine());
        strs = br.readLine().split(" ");
        cities = new int[k<<1];
        for (int i = 0; i < 2 * k; i++)cities[i] = Integer.parseInt(strs[i])-1;
        vis = (1<<2*k)-1; 
        dfs(0, 0);
        System.out.println(result);

    }

    /**
    枚举k条边,vis 0代表访问过 1 代表没访问过
    */
    static void dfs(int i, int cur) {
        if (cur >= result) return;
        if (i == k) {
            result = Math.min(cur, result);
            return;
        }
        int s = 0;
         //vis  最近的1
        s = Integer.numberOfTrailingZeros(vis);
        vis-=(1<<s);
        for (int j = s+1; j < 2 * k; j++) {
            if (((vis>>j)&1)==0)continue;
            vis-=(1<<j);
            cur += A[cities[s]][cities[j]];
            dfs(i + 1, cur); //枚举下一条边。
            vis+=(1<<j);
            cur -= A[cities[s]][cities[j]];
        }
        vis+=(1<<s);
    }
}


编辑于 2024-03-05 01:17:26 回复(0)