这个题牛逼里很呐!!!
题目大意:无非就三个条件:
条件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; }