SDNU 1030.烽火台 SPFA


1030.烽火台
Time Limit: 1000 MS    Memory Limit: 32768 KB


Description
烽火台是一种传递信息的手段,通过在烽火台处燃起狼烟,使其他烽火台或要传递到的人看到,从而达到传递信息的目的。
已知有N个烽火台围绕一座城池,每个烽火台k燃起狼烟都需要一定的时间,记为该烽火台燃起狼烟所需的代价Wk,两座能够传递信息的烽火台i、j之间,因为距离等原因也需要一定的时间,记为传递信息的单位代价Vij。
所有烽火台及若干传递信息的路径可以组成一棵树的结构。其中,树根为城池。每个烽火台为一个节点,若两座烽火台之间能够传递信息,则两烽火台之间存在一条边。对于每条边来说,传递信息的代价为 该边所连接的子树中所有点代价的和 × 该边的单位代价。
根据输入数据,构建一棵树,使得所有边的总代价最小。


Input
第一行两个整数N、M(0 <= N,M <= 1000),为节点的数量及所有可能的边的数量。
第二行N个整数,第k个整数代表第k个节点的代价Wk(1 <= Wk <= 2^10)。
之后M行,每行3个整数i、j、v,代表第i个节点能够传递信息给第j个节点,传递信息的代价为v(1 <= v <= 2^10)。
其中,城池为第1个节点。


Output
最小的总代价。
若有节点无法连接到根,则输出:No Answer


Sample Input

7 7
200 10 20 30 40 50 60
1 2 1
2 3 3
2 4 2
3 5 4
3 7 2
3 6 3
1 5 9

Sample Output

1210


    这道题放在SDNU第一页一直无人问津,不知道是什么原因....今天终于把这道题做出来,不用空在第一页上了。

    题意是说从一个点开始向下传递信息,以前刚开始做这个题的时候读了好久题才读懂,用图来表示一下就好理解了。


    首先根据题意作图可以得到上面的图,红色的值表示生起狼烟所需的代价,圈中的数字代表走的路,这个题目数据除了5以外,其余所有路都是已经确定的了。5可走的路有⑥和⑦两种方案,现在算一下就可以得到。

(1)不走⑥

    ①(10+20+30+60+50)×1=170

    ②30×2=60

    ③(20+50+60)×3=390

    ④50×3=150

    ⑤60×2=120

    ⑦40×9=360

    得到和为1250

(2)不走⑦

    ①(10+30+20+40+50+50)X1=210

    ②30×2=60

    ③(20+40+50+60)×3=510

    ④50×3=150

    ⑤60×2=120

    ⑥40×4=160

    得到和为1210

    所以最佳方案为1210。

    这个题读完边想着是一个用最小生成树来做的题目,但是又不能用最小生成树的方法,因为这个题不能从前面就确定点的选择,必须要考虑后面的情况,而且这道题是一个有向图。之后又往最小树形图的方向上想了好久,但仍未找出做法,然后便一直思维定式直到现在,突然在网上发现这道题的做法应该用SPFA最短路的思路来做。

    因为是求后面和的乘积,观察这两组数,每次求一条路都要求出后面的点的代价乘这条路的值,所以其实可以看做是每个点将这个点的代价乘以这个点到根点的距离,最后得出的结果也是一样的。而这一个距离,因为最后得到的是这个点到根点的距离乘代价,代价是不会变的,所以只要保证得到这个点到根点的最短路就可以了。因为根点题目中已经说了一定是1,直接用SPFA就能求出所有点的最短路,这样就把这个问题转化成了最短路的问题了。

    下面AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<climits>
using namespace std;
long long dis[1005];
bool vis[1005];
long long tim[1005];
long long val[1005];
int n,m;
typedef struct
{
	long long x;
	long long y;
}point;

vector<point> p[1005];

long long Spfa(long long start)
{
    queue<long long> Q;
    long long i;
    long long temp;
    long long v,w;
    dis[start]=0;
    vis[start]=1;
    tim[start]++;
    Q.push(start);
    while(!Q.empty())
    {
        temp=Q.front();
        Q.pop();
        for(i=0;i<p[temp].size();i++)
        {
            v=p[temp][i].x;
            w=p[temp][i].y;
            if (dis[temp] + w < dis[v])
            {
                dis[v] = dis[temp] + w;
                if (!vis[v])
                {
                    Q.push(v);
                    vis[v]=1;
                    tim[v]++;
                    if(tim[v]>m)
                        return 0;
                }
            }
        }
        vis[temp] = 0;
    }
    return 1;
}

int main()
{
    int i;
    long long from,to,distance;
    long long ans;
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        point node;
        memset(dis,0,sizeof(dis));
        memset(vis,0,sizeof(vis));
        memset(tim,0,sizeof(tim));
        for(i=1;i<=m;i++)
        {
            scanf("%lld",&val[i]);
        }
        for(i=0;i<n;i++)
        {
            scanf("%lld%lld%lld",   &from,&to,&distance);
            node.x=to;
            node.y=distance;
            p[from].push_back(node);
            node.x=from;
            p[to].push_back(node);
        }
        for(i=1;i<=m;i++)
        {
            dis[i]=LLONG_MAX;
            vis[i]=0;
            tim[i]=0;
        }
        int flag=0;
        flag=Spfa(1);
        ans=0;
        for(i=2;i<=m;i++)
        {
            ans+=val[i]*dis[i];
            //cout<<ans<<"+="<<val[i]<<"*"<<dis[i]<<endl;
            if(dis[i]==LLONG_MAX)
            {
                flag=0;
                break;
            }
        }
        if(flag)
            cout<<ans<<endl;
        else
            cout<<"No Answer"<<endl;
        for(i=1;i<=m;i++)
            p[i].clear();
    }
    return 0;
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值