题目链接:SDUT 2023 summer team contest(for 22) - 14 - Virtual Judge (vjudge.net)
题意:给你若干条航线,要么两边都有贵宾室,要么只有一个,要么一个也没有。求最小的数量。
思路: 可以先将一定有两个或没有贵宾室的节点染色,没有染为1,有染为2,然后对其他点进行的两次dfs,一次假设染,一次假设没染,第二次dfs经过的点之后将不再遍历,求出两次的dfs合法结果的最小值,如果两次都不合法就输出:”impossible“。将所有的最小值加起来,最后再加上一开始染为2的节点,就是题目要求的最小贵宾室数量。
#include <bits/stdc++.h>
#define dbug(x) cout<<#x<<'='<<x<<endl;
#define x first
#define y second
#define pb push_back
const int N = 2e5 + 5;
const int mod = 1e9 + 7;
using namespace std;
typedef pair<int, int>pii;
int n, m;
map<int, int>mp;
pii kk[N];int ce=0;
int b[N],vis[N],ch[N],x,y,k,cc,f,cnt;
vector<int>g[N];
void dfs(int u){
for(auto v:g[u]){
if(!vis[v]){//如果该点没经过
vis[v]=3-vis[u];//染成和上一个点不一样的颜色
b[++cc]=v;//记录经过了那几个点,方便第一次dfs时将经过变为未经过
if(vis[v]==2)//如果为2,则有一个贵宾室
k++;
dfs(v);
}
else if(vis[v]+vis[u]!=3)f=1;//证明该方法非法
}
}
void solve () {
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
if(w==2){
if(vis[v]==1||vis[u]==1){//如果已经被染为没有,则该图非法
cout<<"impossible"<<endl;return;
}
vis[u]=vis[v]=2;
}
if(w==0){
if(vis[v]==2||vis[u]==2){//如果已经被染为有,则该图非法
cout<<"impossible"<<endl;return ;
}
vis[u]=vis[v]=1;
}
if(w==1){//记录两边只有一个贵宾室的航线
kk[++ce]={u,v};
}
g[u].pb(v);//建图
g[v].pb(u);
}
for(int i=1;i<=ce;i++){//判断两边只有一个贵宾室的航线是否真有一个,如果不是则非法
if(vis[kk[i].x]+vis[kk[i].y]==4||vis[kk[i].x]==vis[kk[i].y]&&vis[kk[i].x]==1){
cout<<"impossible"<<endl;return;
}
}
for(int i=1;i<=n;i++)//记录必定有贵宾室的节点数量
if(vis[i]==2)cnt++;
for(int i=1;i<=n;i++){//k是每次dfs会有多少贵宾室,f是判断该节点染色或不染色是否合法
if(!vis[i]){//如果未被染色
f=x=y=k=cc=0;k=0;x=mod;
vis[i]=1;//假设该点没有贵宾室
dfs(i);
if(f==1)k=mod;//每次不合法k都会变为一个极大的数
x=k;
for(int i=1;i<=cc;i++)vis[b[i]]=0;//将此次dfs经过的点变为没有经过
k=1;//因为该点要被染为2,所以k初始为1
f=0;
vis[i]=2;// 假设该点有贵宾室
dfs(i);
if(f==1)k=mod;
x=min(k,x); //取最小值
if(x!=mod)
cnt+=x;
else{//都不合法则输出impossible
cout<<"impossible"<<endl;return;
}
}
}
cout<<cnt;
}
signed main () {
int T = 1;
std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//cin>>T;
while (T --) solve ();
return 0;
}