Codeforces 760C:Pavel and barbecue(DFS+思维)
http://codeforces.com/problemset/problem/760/C
题意:有n个盘子,每个盘子有一块肉,当肉路过这个盘子的时候,当前朝下的这一面会被煎熟,每个盘子有两个数,pi代表接下来去第pi个盘子,bi为1的话,这块肉会被反面,0的话这块肉不变。对于每块肉,要让它的两面都在每个盘子里面煎过,问最少的修改次数。
思路:有两个条件:1、让图变得连通最少需要修改的方案。2、要让肉两边都被煎到要怎么考虑。
1、这个简单就是一遍DFS求出连通块数,如果连通块数是1的话,就不需要修改,如果大于1的话,那么需要修改的次数为连通块数。因为我们要把一个连通块的其中一条边指出去到下一个连通块。
2、要让肉两边都煎到,我们可以考虑成从原点出发,下一次回到原点的时候肉要是翻面的。那么,如果这样考虑,我们就只需要让翻面的次数为奇数,这样就可以保证回到原点的时候肉是翻面的了。假设为现在的图已经是连通的了,因为只有n条边,所以每个点都必定有且仅有走过一遍,那么该路径翻面的次数其实就是原来输入的1的数目,所以如果输入的1的数量为偶数的话,答案需要+1,让其变成奇数。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define N 200010 4 #define INF 0x3f3f3f3f 5 int p[N], vis[N]; 6 // 1的个数为偶数的时候无法跑到原点是反面,所以要让1的个数变成奇数 7 // 还有要让图变成联通的,当图已经是联通的时候,不需要变化.当图不联通的话,要修改的边数为联通块数,即将该块的一条边连到另一个联通块 8 void DFS(int u) { 9 vis[u] = 1; 10 if(!vis[p[u]]) DFS(p[u]); 11 } 12 13 int main() { 14 int n; 15 scanf("%d", &n); 16 int cnt = 0, ans = 0, b; 17 for(int i = 1; i <= n; i++) scanf("%d", &p[i]); 18 for(int i = 1; i <= n; i++) { 19 scanf("%d", &b); 20 if(b == 1) cnt++; 21 } 22 if(cnt % 2 == 0) ans++; 23 cnt = 0; 24 for(int i = 1; i <= n; i++) { 25 if(!vis[i]) { 26 cnt++; 27 DFS(i); 28 } 29 } 30 if(cnt == 1) cnt--; 31 ans += cnt; 32 cout << ans << endl; 33 return 0; 34 }