• HDU 2017女生赛04 (变形最短路)


    Deleting Edges

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
    Total Submission(s): 263    Accepted Submission(s): 85


    Problem Description
    Little Q is crazy about graph theory, and now he creates a game about graphs and trees.
    There is a bi-directional graph with n nodes, labeled from 0 to n1. Every edge has its length, which is a positive integer ranged from 1 to 9.
    Now, Little Q wants to delete some edges (or delete nothing) in the graph to get a new graph, which satisfies the following requirements:
    (1) The new graph is a tree with n1 edges.
    (2) For every vertice v(0<v<n), the distance between 0 and v on the tree is equal to the length of shortest path from 0 to v in the original graph.
    Little Q wonders the number of ways to delete edges to get such a satisfied graph. If there exists an edge between two nodes i and j, while in another graph there isn't such edge, then we regard the two graphs different.
    Since the answer may be very large, please print the answer modulo 109+7.
     

    Input
    The input contains several test cases, no more than 10 test cases.
    In each test case, the first line contains an integer n(1n50), denoting the number of nodes in the graph.
    In the following n lines, every line contains a string with n characters. These strings describes the adjacency matrix of the graph. Suppose the j-th number of the i-th line is c(0c9), if c is a positive integer, there is an edge between i and j with length of c, if c=0, then there isn't any edge between i and j.
    The input data ensure that the i-th number of the i-th line is always 0, and the j-th number of the i-th line is always equal to the i-th number of the j-th line.
     

    Output
    For each test case, print a single line containing a single integer, denoting the answer modulo 109+7.
     

    Sample Input
    2 01 10 4 0123 1012 2101 3210
     

    Sample Output
    1 6
     

    Source
     

    题意:

    n个点,n*2-n条边,删除掉只剩下n-1条边,满住剩下的每个点在原图中都是最短路的存在。

    分析:

    dij队列优化需要vis,可以避免重复入队的情况,但是不加vis也不会无尽循环下去。




    来自某个博客其他人的解法,其实也不需要跑完全图。



    说入度乘积我更倾向于说成每个点走法的乘积。(5.15重温这句话发现这句话还是有问题的,到三点的还有一种画法)

    dij算法就是求原点到各个点的最短路,也很贴合这道题的性质。那么不同走法之间会有影响吗?只有两个独立事件同时发生没有影响才能相乘。(有问题)

    结果是没有影响。原题中说只要存在任意一条不同的边就是不同的图。


    dij最短路的原理就是通过不同路来松弛。每种走法的组合一定可以存在一条异边。第三个点也是建立在前面点基础上通过扩展而来。

    因为n为50,所以随便哪个最短路算法都能过吧:

    正插邻接表加队列的确比反插好写多了。。。

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <vector>
    #include <bits/stdc++.h>
    #define mod 1000000007
    using namespace std;
    typedef long long ll;
    const int maxn = 100+10;
    const int INF = 0x3f3f3f3f;
    char ch[60][60];
    int cnt[maxn];
    struct node{
        int x,d;
        node(){}
        node(int a,int b){x=a;d=b;}
        bool operator < (const node & a) const
        {
            return d > a.d;
        }
    };
    vector<node> eg[maxn];
    int dis[maxn];
    void Dijkstra(int s)
    {
    
    
        dis[s]=0;
        //用优先队列优化
        priority_queue<node> q;
        q.push(node(s,dis[s]));
        while(!q.empty())
        {
            node x=q.top();q.pop();
            //最后一个点就可以跳出了
            if(x.x==n-1)
                break;
            for(int i=0;i<eg[x.x].size();i++)
            {
                node y=eg[x.x][i];
                if(dis[y.x]>x.d+y.d)
                {
                    cnt[y.x] = 1;
                    dis[y.x]=x.d+y.d;
                    q.push(node(y.x,dis[y.x]));
                }
                else if(dis[y.x]==x.d+y.d) {
                    cnt[y.x]++;
                }
            }
        }
    }
    int main()
    {
    //    freopen("in.txt","r",stdin);
        int n;
        while(~scanf("%d",&n))
        {
            for(int i=0;i<=n;i++)
                dis[i]=INF;
            memset(cnt,0,sizeof(cnt));
            cnt[0]=1;
            for(int i=0;i<=n;i++) eg[i].clear();
    
                for(int i=0;i<n;i++)
                {
                    scanf("%s",ch[i]);
                    for(int j=0;j<n;j++)
                    {
                        if(ch[i][j]!='0')
                            eg[i].push_back(node(j,ch[i][j]-'0'));
                    }
                }
    
            Dijkstra(0);
            ll ans = 1LL;
            for(int i = 0; i < n; i++) {
                ans *= cnt[i];
                ans %= mod;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    

    一开始超时的,不知道在哪里:

    反正以后就用vector写了。。。。

    #include <cstdio>
    #include <cmath>
    #include <cctype>
    #include <algorithm>
    #include <cstring>
    #include <utility>
    #include <string>
    #include <iostream>
    #include <map>
    #include <set>
    #include <vector>
    #include <queue>
    #include <stack>
    
    
    typedef long long ll;
    using namespace std;
    const int maxn = 60+10;
    const int INF = 0x3f3f3f3f;
    //int mp[maxn][maxn];
    int first[maxn];
    int cnt[maxn];
    int num,dis[100];
    char ch[60][60];
    int vis[60];
    
    #define mod 1000000007
    
    struct Node {
        int id;
        int val;
        }node;
    
    struct Edge {
        int id;//以此点为出边找边
        int val;
        int next;
        }e[maxn];
    
    void add(int u,int v,int d) {
        //num边的编号
        e[num].id  = v;
        e[num].val = d;
        e[num].next = first[u];
        first[u] = num;
        num++;
    }
    
    priority_queue<Node> q;
    
    bool operator < (Node a,Node b) {
        return a.val > b.val;
    }
    
    int main() {
    //    freopen("in.txt","r",stdin);
        int n,m;
        while(~scanf("%d",&n)) {
            memset(first,-1,sizeof(first));
            memset(cnt,0,sizeof(cnt));
            while(!q.empty()) q.pop();
    
            for(int i=0;i<n;i++)
            {
                scanf("%s",ch[i]);
                for(int j=0;j<n;j++)
                {
                    if(ch[i][j]!='0')
                        add(i,j,ch[i][j]-'0');
                }
            }
                for(int i = 1; i <= n; i++) {
                    dis[i] = INF;
                }
                Node cur;
                dis[0] = 0;
                node.id = 0;
                node.val = 0;
                q.push(node);
                cnt[0] = 1;
                while(!q.empty()) {
    //                if(cur.id == End){
    //                    break;
    //            }
                    cur = q.top();
                    q.pop();
                    //i为边的编号
                    for(int i = first[cur.id]; i != -1; i = e[i].next) {
                        if(dis[e[i].id] > e[i].val+cur.val) {
                            cnt[e[i].id] = 1;
                            dis[e[i].id] = e[i].val+cur.val;
                            node.id = e[i].id;
                            node.val = dis[e[i].id];
                            q.push(node);
                        }
                        else if(dis[e[i].id] == e[i].val+cur.val)
                            cnt[e[i].id]++;
                    }
            }
            ll ans = 1LL;
            for(int i = 0; i < n; i++) {
                ans *= cnt[i];
                ans %= mod;
            }
    //        for(int i = 0; i <= n; i++) {
    //            printf("初始点到%d点的距离为%d
    ",i,dis[i]);
    //        }
            printf("%d
    ",ans);
            }
            return 0;
        }



  • 相关阅读:
    图表引擎AChartEngine 一
    最优二叉树(哈夫曼树)知识点
    IO流_PrintWriter(字符打印流)与PrintStream(字节打印流)
    Properties集合_练习
    Properties集合_修改配置信息
    Properties集合_list方法与store方法
    Properties集合概述与存和取
    File类_删除一个带内容的目录_练习
    递归
    File类_深度遍历文件夹_练习
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/7256608.html
Copyright © 2020-2023  润新知