P2962 [USACO09NOV]Lights G

https://www.luogu.com.cn/problem/P2962

题目描述

给出一张 n 个点 m 条边的无向图,每个点的初始状态都为 0。

 

你可以操作任意一个点,操作结束后所有相邻的端点的状态都会改变,由 0 变成 1 或由 1 变成 0。

 

你需要求出最少的操作次数,使得在所有操作完成之后所有 n个点的状态都是 1。

 

输入格式

第一行两个整数 n, m。

 

之后 m行,每行两个整数 a, b,表示在点 a, b 之间有一条边。

 

输出格式

一行一个整数,表示最少需要的操作次数。

 

输入输出样例

输入 #1

5 6 

1 2 

1 3 

4 2 

3 4 

2 5 

5 3 

输出 #1

说明/提示

对于 100\% 的数据,1\le n\le35,1\le m\le595, 1\le a,b\le n1≤n≤35,1≤m≤595,1≤a,b≤n。

解:对每个灯来说,它的状态是由相邻的灯是否按下,和自己是否按下决定的。可以发现灯按两次是相当于没按,所以灯亮灭只于决定它的其他灯按下次数的奇偶决定,所以就可以用异或计算了。每个灯是否按下的状态记为x,xi=1表示第i个灯按下,xi=0表示没按下。

这样就可以对每一个灯列一个方程。比如灯1与灯2,4相邻,则关于灯1状态的方程为1*x1 ^ 1*x2 ^ 0*x3 ^ 1*x4 =1(最终状态要为1)。

列出n个方程后,进行高斯消元,如果方程含有自由元,则枚举每个自由元的状态。计算答案即可。

#include<cstdio>
// #include<iostream>
// #include<deque>
// #include<cstring>
// #include<cmath>
// #include<map>
// #include<vector>
// #include<stack>
// #include<algorithm>
// #include<queue>
// #include<set>
#include<bits/stdc++.h>
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(register int i=a;i<=b;i++)
#define fd(i,a,b) for(register int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
#define dbug printf("bbbk\n")
#define R register
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=1e3+99;
const int MN=1e7+9;
const ll mod=1e9+7;
const int MAX=1e9;
const ll INF=1e9+7;
int n,m,ans;
int a[N][N],b[N][N];
bitset<N> p[N];
int sta[N];
inline bool guass()
{
    bool frre=0;//是否含自由元
    for(int now=1;now<=n;now++)//当前主元
    {
        int t=now+1;
        while(t<=n&&!p[t][now]) t++;
        if(t>n) {frre=1;continue;};
        swap(p[t],p[now]);
        fu(i,1,n)
        {
            if(i==now) continue;
            if(p[i][now]) p[i]^=p[now];
        }
    }//判断k是不是自由元只要看p[k][k]==1?
    return frre;
}
inline void dfs(int x,int num)
{
    if(num>=ans) return;//剪枝,ans取最小值
    if(x==0) {ans=min(ans,num);return;}
    if(p[x][x])//x不是自由元
    {
        int st=p[x][0];
        //每个非自由元都只受后面的自由元影响
        fu(i,x+1,n) if(p[x][i]) st^=sta[i];
        sta[x]=st; 
        dfs(x-1,num+st);
    }
    else//有自由元,枚举自由元状态
    {
        dfs(x-1,num);
        sta[x]=1;
        dfs(x-1,num+1);
        sta[x]=0;
    }
}
int main()
{
    //freopen("P2962_12.in","r",stdin);
    cin>>n>>m;
    fu(i,1,n) p[i][0]=1,p[i][i]=1;//第0列作为增广列
    fu(i,1,m)
    {
        int u,v;cin>>u>>v;
        p[u][v]=1;p[v][u]=1;
    }
    if(!guass())//有唯一解
    {
        fu(i,1,n) if(p[i][0]) ans++; 
    }
    else//有自由元,枚举自由元状态
    {
        ans=INF;
        dfs(n,0);
    }
    cout<<ans<<endl;
    return 0;
}

 

题目描述

给出一张 nn 个点 mm 条边的无向图,每个点的初始状态都为 00

你可以操作任意一个点,操作结束后所有相邻的端点的状态都会改变,由 00 变成 11 或由 11 变成 00

你需要求出最少的操作次数,使得在所有操作完成之后所有 nn 个点的状态都是 11

输入格式

第一行两个整数 n, mn,m

之后 mm 行,每行两个整数 a, ba,b,表示在点 a, ba,b 之间有一条边。

输出格式

一行一个整数,表示最少需要的操作次数。

输入输出样例

输入 #1
5 6 
1 2 
1 3 
4 2 
3 4 
2 5 
5 3 
输出 #1
3 

说明/提示

对于 100\%100% 的数据,1\le n\le35,1\le m\le595, 1\le a,b\le n1n35,1m595,1a,bn

posted on 2021-04-24 23:54  Aminers  阅读(148)  评论(0编辑  收藏  举报

导航