[USACO09NOV]Lights G
题目描述
给出一张n个点n条边的无向图,每个点的初始状态都为0。
你可以操作任意一个点,操作结束后该点以及所有与该点相邻的点的状态都会改变,由0变成1或由1变成0。
你需要求出最少的操作次数,使得在所有操作完成之后所有n个点的状态都是1。
输入格式
第一行两个整数n, m
之后m行,每行两个整数a, b,表示在点a, b之间有一条边。
输出格式
一行一个整数,表示最少需要的操作次数。
样例
样例输入
5 6
1 2
1 3
4 2
3 4
2 5
5 3
样例输出
3
思路
使用Meet In The Middle,先将1~mid点的全部情况遍历,将可能得到的结果以及到达对应结果状态所需要的最少步数记录下来,由于1<<35过大,所以推荐使用map进行存储。
将前半部分遍历完成后遍历后半部分,对于每个操作可以得到一个最终状态,将最终状态与全1情况异或可以得到对应的互补状态,然后维护最小操作次数即可。
易错点
1.左移的时候记得使用1ll,不然会爆int。
2.需要初始化map[0] = 0,不然当后半能达到全1状态时会多加上几次操作(前半到达全0的次小操作次数)
#include<bits/stdc++.h>
using namespace std;
#define int long long
vector<int> edge[50];
int N, M, ans;
map<int, int> half;
bool option(int op, int n)
{
//查看是否对第n位进行操作
return (op >> n) & 1;
}
int answer(int a)
{
//得到互补状态
return ans ^ a;
}
bool getState(int op, int n)
{
/*
判断当前位在某种操作下最终状态是0还是1
*/
int res = 0;
if(option(op, n)){
res += 1;
}
for(int i = 0; i < edge[n].size(); i++){
if(option(op, edge[n][i])) res += 1;
}
return (res & 1)?true:false;
}
int getOptionTime(int op)
{
/*
获得一个操作的总操作次数
*/
int res = 0;
while(op){
res += (op & 1);
op >>= 1;
}
return res;
}
int setStateEnd(int op)
{
/*
获得一个操作对应的最终状态,返回对应二进制数
*/
int res = 0;
for(int i = 0; i < N; i++){
if(getState(op, i)){
res ^= (1ll << i);
}
}
return res;
}
/*void print(int a)
{
stack<int> s;
while(a){
s.push(a & 1);
a >>= 1;
}
while(s.size()){
cout << s.top();
s.pop();
}
cout <<endl;
}*/
signed main()
{
cin >> N >> M;
half[0] = 0;// 维护map[0] = 0
ans = (1ll << N) - 1;// ans对应的最终想要的状态,用于与得到的状态异或来获取另外的部分
int minTime = 0x3f3f3f3f;
for(int i = 0; i < M; i++){// 建边
int a, b;
cin >> a >> b;
edge[--a].push_back(--b);
edge[b].push_back(a);
}
int mid = N >> 1ll;
for(int op = 0; op <= (1ll << (N/2))-1; op++){// 枚举前半部分的全部操作情况
int res = setStateEnd(op);// 得到操作情况对应的结果状态
//维护最小操作次数
if(half.count(res)){
half[res] = min(half[res], getOptionTime(op));
}
else{
half[res] = getOptionTime(op);
}
}
for(int op = 0; op <= (1ll << N)-(1ll << (N/2)); op += (1ll << (N/2))){// 枚举后半部分的全部操作情况
int res = setStateEnd(op);
int thisAns = answer(res);// 判断当前状态的互补状态
if(half.count(thisAns)){// 查看是否存在互补状态,若存在,则维护最小值
int nowTime = half[thisAns];
nowTime += getOptionTime(op);
minTime = min(minTime, nowTime);
}
}
/*cout << "------" << endl;
print(setStateEnd((1ll << 33) + (1ll << 34)));
print(ans);
cout << "------" << endl;
for(int i = 0; i < N; i++){
cout << i << "---->";
for(int j = 0; j <edge[i].size(); j++){
cout << edge[i][j] << ' ';
}
cout << endl;
}*/
cout << minTime <<endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现