http://acm.timus.ru/problem.aspx?space=1&num=1124
连通分量 Tarjan
大体题意:
n个箱子 每个里面有m个物品 但对应编号有可能错误 要求把所以物品归位 求最少move
一次move 有两种情况 1,把一个物品从一个箱子里拿到里一个箱子了(回来,不会来都可以) 2,空手从一个箱子到另一个箱子
思路:
首先每个放错的物品都得被移动一次 然后就是从一个连通分量移动到另一个连通分量(空手移动)
注意移动到第一个连通分量无需花费时间 而已要注意为0的情况
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<vector> #include<map> #include<cmath> #include<queue> #include<stack> #include<algorithm> #define LL long long //#pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; const int INF=0x3f3f3f3f; const int N=505; bool side[N][N]; int low[N],dfn[N],deep; bool in[N],visited[N]; int difnum[N]; stack<int>St; int n,m; void Tarjan(int x) { low[x]=dfn[x]=deep++; visited[x]=true; St.push(x); in[x]=true; for(int l=1;l<=n;++l) { if(side[x][l]==false) continue; if(!visited[l]) { Tarjan(l); low[x]=min(low[x],low[l]); }else if(in[l]) { low[x]=min(low[x],dfn[l]); } } if(low[x]==dfn[x]) { while(St.top()!=x) { in[St.top()]=false; St.pop(); } in[St.top()]=false; St.pop(); } } int main() { //freopen("data.txt","r",stdin); while(cin>>n>>m) { memset(side,false,sizeof(side)); memset(difnum,0,sizeof(difnum)); int ans=0; for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { int l; cin>>l; if(l!=i) { ++ans; side[i][l]=true; ++difnum[i]; } } } memset(visited,false,sizeof(visited)); deep=0; for(int i=1;i<=n;++i) { if(!visited[i]&&difnum[i]>0) { Tarjan(i); ++ans; } } if(ans>0) --ans; cout<<ans<<endl; } return 0; }