1529: [POI2005]ska Piggy banks
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 563 Solved: 224
[ Submit][ Status][ Discuss]
Description
Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐.
Input
第一行一个整数 N (1 <= N <= 1.000.000) – 表示存钱罐的总数. 接下来每行一个整数,第 i+1行的整数代表第i个存钱罐的钥匙放置的存钱罐编号.
Output
一个整数表示最少打破多少个存钱罐.
Sample Input
4
2
1
2
4
2
1
2
4
Sample Output
2
In the foregoing example piggy banks 1 and 4 have to be smashed.
一道OI题,两种方法,一种是求强连通,统计入度为0的连通分量个数即可,恶心的用栈手工模拟非递归版的强连通。(数学模型就是:给定n个点m条边,求最少要从几个点出发,可以遍历整个图。)
另一种方法就是把这个当作无向图,只会求一次并查集。
第一种方法代码:
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000001;
const int maxm = 2000001;
typedef struct node
{
int v;
node *next;
} node;
node edge[maxm], *g[maxn], *w[maxn];
int n, m, len, c, cnt, top;
int post[maxn], deg[maxn], sc[maxn], stack[maxn];
bool s[maxn];
inline void addg(int u, int v)
{
edge[len].v = v;
edge[len].next = g[u];
g[u] = &edge[len++];
}
inline void addw(int u, int v)
{
edge[len].v = v;
edge[len].next = w[u];
w[u] = &edge[len++];
}
int slove()
{
int i, j, k, u;
cnt = 0;
for (i = 1; i <= n; ++i) s[i] = deg[i] = 0;
for (i = 1; i <= n; ++i)
if (!s[i])
{
top = 0;
stack[top++] = i;
s[i] = 1;
while (top)
{
k = 0;
u = stack[top - 1];
for (node *p = w[u]; p; p = p -> next)
if (!s[p -> v])
{
s[p -> v] = 1;
k = 1;
stack[top++] = p -> v;
}
if (!k)
{
post[cnt++] = stack[top - 1];
top--;
}
}
}
cnt = c = 0;
for (i = 1; i <= n; ++i) s[i] = 0;
for (i = n - 1; i >= 0; --i)
if (!s[post[i]])
{
top = 0;
stack[top++] = post[i];
while (top)
{
u = stack[top - 1];
top--;
s[u] = 1;
sc[u] = cnt;
for (node *p = g[u]; p; p = p -> next)
if (!s[p -> v])
stack[top++] = p -> v;
}
cnt++;
}
for (u = 1; u <= n; ++u)
for (node *p = g[u]; p; p = p -> next)
if (sc[u] != sc[p -> v])
deg[sc[p -> v]]++;
for (i = 0; i < cnt; ++i)
if (deg[i] == 0)
c++;
return c;
}
int main()
{
int i, j, k, u, v;
scanf("%d", &n);
len = 0;
for (i = 1; i <= n; ++i)
g[i] = w[i] = NULL;
for (v = 1; v <= n; ++v)
{
scanf("%d", &u);
addg(u, v);
addw(v, u);
}
printf("%d\n", slove());
return 0;
}
并查集代码:
#include <stdio.h>
#define MAXN 1000005
int parent[MAXN];
int findset(int x)
{
int temp,px=x;
while (parent[px]>=0)
px=parent[px];
while (parent[x]>=0)
{
temp=parent[x];
parent[x]=px;
x=temp;
}
return px;
}
void unionset(int x,int y)
{
int temp;
x=findset(x);
y=findset(y);
if (x==y) return;
temp=parent[x]+parent[y];
if (parent[x]>parent[y])
{
parent[y]=temp;
parent[x]=y;
}
else
{
parent[x]=temp;
parent[y]=x;
}
return;
}
int main()
{
int n,t,i,j,count,x,y;
scanf("%d", &n);
count = 0;
for (i=1;i<=n;++i)
parent[i]=-1;
for (int i = 1; i <= n; ++i)
{
int x;
scanf("%d", &x);
unionset(i, x);
}
for (i = 1; i <= n; ++i)
if (parent[i] < 0) count++;
printf("%d\n", count);
}