可能是因为这次没有分Div.1和Div.2,所以感觉题的难度比较大。
题意:
给出一个1~n的排列和一个邻接矩阵A,Aij = 1表示可以交换排列的第i项和第j项,问经过若干次交换后,求能够得到最小字典序的排列。
分析:
如果a和b可交换,b和c可交换,则a和c也可以交换位置。如果把这n个位置看做顶点,两个可交换的位置连一条边,则图中在同一连通分量的顶点都是可以交换元素的。所以用并查集做就很方便了。
要想得到字典序最小的排列,直接贪心就可以了。从第一个数开始,首先试试1能不能交换到第一个位置去,否则尝试2,一直到能交换或者没有比开头的数更小的数位置。然后继续尝试第二个数。代码中有个used标记数组,标记这个数是否在前面用过。
#include <cstdio>
#include <algorithm> const int maxn = + ;
char G[maxn][maxn];
int p[maxn], a[maxn], pos[maxn];//pos记录每个数的位置
bool used[maxn];//标记每个数是否用过 int GetParent(int x)
{
return (p[x] == x ? x : p[x] = GetParent(p[x]));
} void Union(int x, int y)
{
int px = GetParent(x), py = GetParent(y);
if(px != py)
p[px] = py;
} int main()
{
//freopen("in.txt", "r", stdin);
int n;
scanf("%d", &n);
for(int i = ; i <= n; ++i) p[i] = i;
for(int i = ; i <= n; ++i) scanf("%d", &a[i]);
for(int i = ; i <= n; ++i) scanf("%s", G[i] + ); for(int i = ; i <= n; ++i) pos[a[i]] = i; for(int i = ; i <= n; ++i)
for(int j = ; j <= n; ++j)
if(G[i][j] == '') Union(i, j); for(int i = ; i < n; ++i)
{
used[a[i]] = true;
for(int j = ; j < a[i]; ++j)
{
if(used[j]) continue;
if(GetParent(i) == GetParent(pos[j]))
{
used[a[i]] = false;
used[j] = true;
int q = pos[j];
std::swap(a[i], a[q]); //交换两元素
std::swap(pos[a[i]], pos[a[q]]); //同时交换每个数的位置
break;
}
}
} for(int i = ; i < n; ++i) printf("%d ", a[i]);
printf("%d\n", a[n]); return ;
}
代码君