Q - 0 or 1 HDU - 4370 (spfa最短路+最小环)

这个题牛逼里很呐!!!

题目大意:无非就三个条件:

条件1 :第一行从2到n相加为1

条件2 :第n列从第一行到倒数第二行的数相加为1

条件3:除了第一列和第一行还有第n列和第n行的和没有要求外,其余第i行相加要等于第i列相加。

题目要求最小的ΣCij * X ij(1 <= i,j <= n)

我们可以先将条件转换一下,条件1 可以转换为从1到i的一条边,条件2可以转换为从i到n的一条边。那么答案就变成了从1到n的一条路径,也就是最短路径。

再说一下条件3,第i行等于第i列,假如说我们在第k行第j列挑了一个数,那就必须在第k列再挑一个数,如果你挑的这个数是第k列第j行的数,好,那么可以停止挑选了,如果不是,假如说挑的是第x行的数,那就必须在第x列在挑,就这样一直挑下去。我们在来看(K,J)-->(J,K)是不是形成了一个环呢?也就是说从1出发在到1的一个环,当然不能是(1,1)-->(1,1)。然后n同理。

所以答案就是从1到n的最短路,从1到1的最小环+从n到n的最小环取最小值,就好了。

关于求最小环的算法,以前学过一个floyd的算法...不记得了。今天又学了一手spfa的骚操作。既可以求最短路也可以求单源最短环。

code:

#include<bits/stdc++.h>
using namespace std;
const long long  INF=0x3f3f3f;
const int N=300+7;
long long  arr[N][N];
long long  dis[N];
int n;
bool mark[N];
void spfa(int s){ 
    queue<int>que;
    for(int i=1;i<=n;i++){
        mark[i]=0;
        if(i==s) dis[i]=INF;
        else {
            dis[i]=arr[s][i];
            mark[i]=1;
            que.push(i);
        }
    }
    while(que.size()){
        int u=que.front();
        que.pop();
        mark[u]=0;
        for(int i=1;i<=n;i++){
            if(dis[i]>dis[u]+arr[u][i]){
                dis[i]=dis[u]+arr[u][i];
                if(mark[i]==0){
                    mark[i]=1;
                    que.push(i);
                }
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(0);
    while(cin>>n){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                cin>>arr[i][j];
        spfa(1);
        long long  ans=dis[n];
        long long  anss1=dis[1];
        spfa(n);
        long long anssn=dis[n];
        cout<<min(ans,anss1+anssn)<<endl;
    }
    return 0;
}

相关推荐