0 or 1Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4398 Accepted Submission(s): 1473 Problem Description Given a n*n matrix Cij (1<=i,j<=n),We want to find a n*n matrix Xij (1<=i,j<=n),which is 0 or 1.Besides,Xij meets the following conditions: 1.X12+X13+...X1n=1 2.X1n+X2n+...Xn-1n=1 3.for each i (1<i<n), satisfies ∑Xki (1<=k<=n)=∑Xij (1<=j<=n). For example, if n=4,we can get the following equality: X12+X13+X14=1 X14+X24+X34=1 X12+X22+X32+X42=X21+X22+X23+X24 X13+X23+X33+X43=X31+X32+X33+X34 Now ,we want to know the minimum of ∑Cij*Xij(1<=i,j<=n) you can get. Hint For sample, X12=X24=1,all other Xij is 0.
Input The input consists of multiple test cases (less than 35 case).
Output For each case, output the minimum of ∑Cij*Xij you can get.
|
题意:让求 ∑Cij*Xij 的最小值。
这个题目好有意思。
解析转自dalao
转换思维的一道题,虽然是在最短路的专题里看见的,但是一开始真想不到是最短路,还以为是一个求min( c[1][n], c[1][k] + c[k][n] )( 1 < k < n )的水题,不过认真看完题解发现转换思维之后确实是一道水题,难就难在转换思维
理解条件之前先转换一下思维,将矩阵C看做描述N个点花费的邻接矩阵
再来看三个条件:
条件一:表示1号点出度为1
条件二:表示n号点入度为1
条件三:表示k( 1 < k < n )号点出度等于入度
最后再来看看题目要求,∑Cij*Xij(1<=i,j<=n),很明显,这是某个路径的花费,而路径的含义可以有以下两种:
一:1号点到n号点的花费
二:1号点经过其它点成环,n号点经过其它点成环,这两个环的花费之和
于是,就变成了一道简单的最短路问题
关于环花费的算法,可以改进spfa算法,初始化dis[start] = INF,且一开始让源点之外的点入队
dalao 的代码:
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<cstdio>
#include<queue>
#include<stack>
using namespace std;
const int INF = 0x3f3f3f3f;
int cost[305][305];
int dis[305];
int n;
bool vis[305];
void spfa( int start ){
stack<int> Q;
for( int i = 1; i <= n; i++ ){
dis[i] = cost[start][i];
if( i != start ){
Q.push( i );
vis[i] = true;
}
else{
vis[i] = false;
}
}
dis[start] = INF;
while( !Q.empty() ){
int x = Q.top(); Q.pop(); vis[x] = false;
for( int y = 1; y <= n; y++ ){
if( x == y ) continue;
if( dis[x] + cost[x][y] < dis[y] ){
dis[y] = dis[x] + cost[x][y];
if( !vis[y] ){
vis[y] = true;
Q.push( y );
}
}
}
}
}
int main(){
ios::sync_with_stdio( false );
while( cin >> n ){
for( int i = 1; i <= n; i++ ){
for( int j = 1; j <= n; j++ ){
cin >> cost[i][j];
}
}
int ans, c1, cn;
spfa( 1 );
ans = dis[n];
c1 = dis[1];
spfa( n );
cn = dis[n];
cout << min( ans, c1 + cn ) << endl;
}
return 0;
}
我的Code:
#include<iostream>
#include<cstring>
#include<queue>
#include<map>
#include<iomanip>
#include<stack>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<algorithm>
using namespace std;
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define sRead(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define gc(x) scanf(" %c",&x);
#define mmt(x,y) memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod 998244353
const int N = 3005;
const int M= 1e6;
int c[305][305];
int d[305];
bool vis[305];
void spfa(int star,int n)
{
mmt(vis,0);
queue<int> Q;
for(int i = 1; i <= n; ++i)
{
if(i == star) continue;
d[i] = c[star][i];
vis[i] = 1;
Q.push(i);
}
d[star] = INF;
while(Q.size())
{
int x = Q.front();
Q.pop();
vis[x] = 0;
for(int i = 1; i <= n; ++i)
{
if(i == x) continue;
if(d[i] >d[x] +c[x][i])
{
d[i] = d[x] + c[x][i];
if(!vis[i])
{
vis[i] = 1;
Q.push(i);
}
}
}
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
mmt(c,0);
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{
read(c[i][j]);
}
}
spfa(1,n);
int ans = d[n];//1 - n最短路情况
int op1 = d[1];//成环的情况
spfa(n,n);
int op2 = d[n];
cout<<min(ans,op1 + op2)<<endl;
}
}