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;
}