這題也A了好久啊……(可能是我太菜了
看到這道題第一眼是懵的,但發現數據範圍很小,稍微放輕鬆一些
第一題比較好解
首先我萌可以將必經之路這樣理解:如果從起點到達終點必須經過它,辣麼如果刪掉這個點,這個圖就不連通了!
因此我萌枚舉每個點 進行bfs(對於點的刪除處理方法很多,這裏窩的方法是在bfs前就把該點的vis設爲true,醬紫似乎很好寫呢,也不會浪費時間複雜度)
----------------------------------------------------第一問完成分割線----------------------------------------------------------
看到第二題的第一反應就是符合條件的這些點必定都符合第一問!!
也沒仔細想怎麼證明就直接準備開始瞎搞了(畢竟這是一道 省選+/noi- 準備隨便玩玩的。。。(泥萌可不能學我否則會和我一樣刷題效率低的要死
然而謝天謝地我的rp終於回來了!!這個想法是對的
後來大概想了一下證明。符合第一問的點必定符合作爲起點/終點的條件很好證明。必經了嘛。。所以肯定都能到。至於其他的點爲什麼不行。。。我也不太說得清楚啊!!(逃
但是!!但是!!坑點來了。終點不通往任何路口。第一遍54分就是坑在這個點QAQ(不過這個嚴格上說也不算坑點其實就是蒟蒻沒有好好審題。。。。
那麼如何保證沒有公共點和公共邊呢?有了前一問的基礎,這題的解法就比較顯然了。若圖能被改點分成不連通的兩個部分即爲所求。
這段代碼寫的比較噁心。。。。詳見註釋
還有一個巨大的坑點!!這玩意兒可以有路通向自己!!最後一個點!!!(當然還有自環。)介個感覺題目裏並沒有說啊。。還是我眼瞎。。?
#include<bits/stdc++.h>
using namespace std;
int n;
bool can[60][60];
vector<int> edge[60];
bool vis[60];
bool before[60][60];//before[i][j]=true表示從起點到達終點的路徑中i在j的前面 且該數組僅更新j爲必經點的情況 詳細用途見後
bool ok(int node) {
queue<int> q;//數組和隊列勿忘初始化 要不然會出大事。。
memset(vis,false,sizeof(vis));
vis[0]=vis[node]=true;//先設爲true這樣bfs時就不會從這個點走
before[0][node]=before[node][node]=true;//其實此處before數組可以完全替代vis
q.push(0);
while(!q.empty()){
int cur=q.front();
if (cur==n-1) return true;//即圖仍連通
vis[cur]=true;
before[cur][node]=true;
q.pop();
for (int i=0;i<edge[cur].size();i++) if (!vis[edge[cur][i]]) q.push(edge[cur][i]);
}
return false;
}
bool check(int node){//注意這個check就是當成無向圖來處理了
int nn=n;
for (int i=0;i<edge[node].size();i++) if (before[edge[node][i]][node]) return false;
//檢查作爲終點是該點是否通向其他點
for (int i=0;i<n;i++) {
//這段代碼比較噁心了。處理點node將圖分爲兩個時邊的情況。
//顯然從起點到終點的路徑上比必經點先經過的點仍能與該點相連
//而其他的點則只能與新“分裂”出的點相連(nn)
if (can[node][i]&&!before[i][node]) {
//這裏就是before函數的主要用途了。可以對照樣圖 理解起來並不困難
can[nn][i]=can[i][nn]=true;
can[node][i]=can[i][node]=false;
}
}
queue<int> q;
memset(vis,false,sizeof(vis));
q.push(0);
while(!q.empty()){//bfs基本和先前類似
int cur=q.front();
if (cur==n-1) {
for (int i=0;i<n;i++) if (can[nn][i]){//數組一定要還原!!下面也是
can[nn][i]=can[i][nn]=false;
can[node][i]=can[i][node]=true;
}
return false;
}
q.pop();
if (vis[cur]) continue;
vis[cur]=true;
for (int i=0;i<=n;i++) if (!vis[i]&&can[cur][i]) q.push(i);
}
for (int i=0;i<n;i++) if (can[nn][i]) {
can[nn][i]=can[i][nn]=false;
can[node][i]=can[i][node]=true;
}
return true;
}
int main(){
while (1){//輸入
bool flag=false;
while(1){
int x;
cin>>x;
if (x==-1) {
flag=true;
break;
}
if (x==-2) break;
if (x!=n) edge[n].push_back(x),can[n][x]=can[x][n]=true;
}
if (flag) break;
n++;
}
vector<int> ans;
for (int i=1;i<n-1;i++) if (!ok(i)) ans.push_back(i);
if(ans.size()) cout<<ans.size()<<" ";//坑點 不然兩個0輸出會在一行
else cout<<0<<endl;
vector<int> ans1;
for (int i=0;i<ans.size();i++) {
if (i==ans.size()-1) cout<<ans[i]<<endl;
else cout<<ans[i]<<" ";
if (check(ans[i])) ans1.push_back(ans[i]);
}
if (ans1.size()) cout<<ans1.size()<<" ";
else cout<<0<<endl;
for (int i=0;i<ans1.size();i++) {
if (i==ans1.size()-1) cout<<ans1[i]<<endl;
else cout<<ans1[i]<<" ";
}
}