小N研究的NP完全问题

64 篇文章 0 订阅
42 篇文章 0 订阅

Description

这里写图片描述

Solution

什么NP完全问题……
这个就是一道水题。
首先我们可以想到建一个图,但是不知所措。
因为每一个点都可以左右选择,那么就是可以把一个奇数状态转移成偶数状态。
那么我们一开始就随便放,然后再一个点连接的相邻的两个点进行连边,表示可以转移状态。
我们会发现很多个联通块,如果联通块里面有奇数个权值为奇数的点,那么就答案只能加一了,很显然。
那么我们怎么找方案数呢?
一个dfs,建出一颗dfs树
一个权值为奇数的点,那么就表示这个点可以向上串1个1。
我们发现子树有奇数个权值为奇数的节点,那么就表示这些1怎么上传都不能化成偶数,那么这个父节点与它向上的连边的这一条边选择的点的方向就要改一下了,好好理解一下,就好了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
const int maxn=200007;
using namespace std;
int i,j,k,l,t,n,m,ans;
int first[maxn*2],next[maxn*2],last[maxn*2],num,shu[maxn*2];
int a[maxn*2],b[maxn*2],c[maxn*2],d[maxn*2],tot;
bool bz[maxn];
void add(int x,int y,int z){
    last[++num]=y;
    next[num]=first[x];
    first[x]=num;
    shu[num]=z;
}
int dfs(int x){
    int i,j=c[x]%2,k;
    bz[x]=1;
    for(i=first[x];i;i=next[i]){
        if(!bz[last[i]]){
            k=dfs(last[i]);
            d[shu[i]]=k%2;
            j+=k;
        }
    }
    return j;
}
int dfs1(int x){
    int i,j=c[x]%2;
    bz[x]=1;
    for(i=first[x];i;i=next[i]){
        if(!bz[last[i]]){
            j+=dfs1(last[i]);
        }
    }
    return j%2;
}
int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        scanf("%d%d",&a[i],&b[i]);
        c[a[i]]++;
    }
    fo(i,1,n){
        add(a[i],b[i],i);
        add(b[i],a[i],i);
    }
    fo(i,1,m){
        if(!bz[i]&&dfs1(i))ans++;
    }
    printf("%d\n",ans);
    memset(bz,0,sizeof(bz));
    fo(i,1,m){
        if(!bz[i])t=dfs(i);
    }
    fo(i,1,n){
        if(d[i]%2==0)printf("%d ",a[i]);
        else printf("%d ",b[i]);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值