A - Amanda Lounges (dfs+二分图染色)

题目链接: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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值