P2752 [USACO4.3]街道賽跑Street Race

這題也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]<<" ";
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章