http://codeforces.com/contest/967/problem/F
题目大意:
有n个点,n*(n-1)/2条边的无向图,其中有m条路目前开启(即能走),剩下的都是关闭状态
定义:从x走到y(即x->y)后,和x所连接的边的所有状态都反转(即开启->关闭,关闭->开启)
问,从起点1走到终点n,最少需要经过几步,并且输出这个路径(如果存在多挑最短路径,输出任意一条)
如果不存在,则输出-1
思路:
其实这道题第一眼看过去就是bfs,不过,由于每个状态都在改变,那要怎么利用这个bfs的特性呢?
后来画了画图,你会发现,存在如下的问题。
对于一个a+1个点,b条边的图(其中终点,即a+1这个点是没有边连向它)来说,如果b=a*(a-1)/2,那么,无论如何都是走不到a+1这个点的。
所以,问题就转换成,如果b<a*(a-1)/2需要走几步。
于是我们可以发现,在b<a*(a-1)/2的情况中,我们又可以找出bb=aa*(aa-1)/2的情况。 其中bb表示b的子集,aa表示a的子集
于是不断剖析下去,最后只会得到两种情况
第一种:
a=3, b=2的情况(注,a=4才是终点,前面定义过了= =)
图形:①->②->③
即该情况步数是4步,即1->2->3->1->4
第二种:
a=4, b=5的情况
(= =)一共4个点,告诉你边,你自己画吧
边为:
1 2
2 3
3 4
4 1
1 3
该情况步数是5
当然,可能存在步数<=4的情况,即从1出发直接按照题目所给边走到终点,这个就不用我说了吧!!!(具体为什么的话,请自己利用上面的两种情况分析一下)
所以根本不存在output中说的le6的情况
对于前面两种情况中,步数=4的情况是容易解决的,关键就是步数=5的情况容易TLE(= =,退役一年没做题,脑子锈逗了,T了一天)
首先我们可以发现步数=5的情况可以转移成——在删除起点1之后,步数=4的情况。
因此,我们只需要找一个从1->u的情况,设u包含u下面所有的子节点(不包含1)个数位C,那么如果该集合的节点个数(包含u)< u的边数,那么就存在解
解为1->u->v->z->u->n
然而= =,楼主先暴力u,再暴力v,再暴力z,T死我了
改成先暴力u,再暴力z,最后通过u、z找v就过了= =
原因是,因为你会发现,v满足的条件比z满足的条件要少,所以v的剪枝比较少,因此先暴力v所要判断的节点数目会更多,因此不优秀
(退役了以后做题是真的伤不起,还写得很麻烦= =,懒得优化了,自己看着办吧)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #pragma comment(linker,"/STACK:102400000,102400000") #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second #define haha printf("haha ") const int maxn = 5e5 + 5; int n, m; vector<int> G[maxn]; bool vis[maxn]; int pre[maxn]; bool bfs(int u){ queue<pair<int, int> > que; que.push(mk(1, 0)); memset(pre, -1, sizeof(pre)); vis[1] = 1; while (!que.empty()){ pair<int, int> p = que.front(); que.pop(); int u = p.fi, step = p.se; //if (u == n) break; for (int i = 0; i < G[u].size(); i++){ int v = G[u][i]; if (vis[v]) continue; vis[v] = true; pre[v] = u; if (step + 1 <= 4){ que.push(mk(v, step+1)); } } } while(!que.empty()) que.pop(); if (pre[n] == -1) return false; int pos = n; vector<int> ans; ans.pb(pos); while (pre[pos] != -1){ ans.pb(pre[pos]); pos = pre[pos]; } reverse(ALL(ans)); if (ans.size() > 5) return false; printf("%d ", ans.size()-1); for (int i = 0; i < ans.size(); i++) printf("%d ", ans[i]); cout << endl; return true; } void display(int a, int b, int c, int d, int e, int f){ printf("5 %d %d %d %d %d %d ", a, b, c, d, e, f); } void bfs2(int x, vector<int> &cache){ vis[x] = 1; queue<int> que; que.push(x); cache.pb(x); while (!que.empty()){ int u = que.front(); que.pop(); for (int i = 0; i < G[u].size(); i++){ if (vis[G[u][i]] || u == 1)continue; vis[G[u][i]] = 1; que.push(G[u][i]); cache.pb(G[u][i]); } } } set<int> mys[maxn]; int main(){ cin >> n >> m; for (int i =1 ;i <= m; i++){ int u, v; scanf("%d%d", &u, &v); G[u].pb(v), G[v].pb(u); mys[u].insert(v); mys[v].insert(u); } if (m == 0) return puts("-1"), 0; if (n == 1) return printf("1 1 "), 0; //direct from 1 to n if (bfs(1)) return 0; memset(vis, 0, sizeof(vis)); //other(just 4 steps) for (int i = 0; i < G[1].size(); i++){//the next point which connect to 1 vis[G[1][i]] = 1; } for (int i = 0; i < G[1].size(); i++){ int u = G[1][i]; for (int j = 0; j < G[u].size(); j++){ int v = G[u][j]; if (v == 1 || vis[v]) continue; printf("4 1 %d %d 1 %d ", u, v, n); return 0; } } //other(just 5 steps) //1->u->v->z->u->n memset(vis, 0, sizeof(vis)); vector<int> cache; for (int i = 0; i < G[1].size(); i++){ int u = G[1][i]; if (vis[u]) continue; cache.clear(); bfs2(u, cache); if (G[u].size() < cache.size()){ for (int j = 0; j < cache.size(); j++){ u = cache[j]; if (!mys[u].count(1)) continue; if (G[u].size() >= cache.size()) continue; for (int k = 0; k < G[1].size(); k++){ int z = G[1][k]; if (!vis[z] || mys[z].count(u) || z == u) continue; if (G[z].size() >= cache.size()) continue; for (int f = 0; f < G[z].size(); f++){ int v = G[z][f]; if (!mys[v].count(1) || !mys[v].count(u)) continue; if (v == 1) continue; display(1, u, v, z, u, n); return 0; } } } } } puts("-1"); return 0; } /* 5 5 1 2 2 3 3 4 4 1 1 3 */