P2962 [usaco09nov] lights G

P2962 [USACO09NOV]Lights G

Luogu

Source: P2962 [USACO09NOV]Lights G

Meaning:

Given n points, the initial value is 0, m undirected edges, operate one point at a time, change itself and the point 1 connected to itself into 0, 0 into 1, and ask the minimum number of operations, so that all are 1

Idea:

I think the bosses are written by Gauss elimination + dfs, but I won't

Greedy: each point can be operated at most once

Violent search: first consider each state of violent search. The time complexity is 2^n, where n is up to 35. Obviously, it will timeout

Two way search: when we use two-way search, the time complexity will be reduced to about n*2^(n/2), which is a lot

The trick is to first define the f array to record the point that the ith point can reach. It can be realized by using the or operation. It can be detailed into a binary number. n is up to 35, so open long long
Put the results of the first half of the search into the hash, and find the hash in the second half of the search. If it happens to match, it is the answer

If you want to easily understand the following code, you can first understand binary and bit operations

#include <iostream>
#include <algorithm>
#include <map>
#include <unordered_map>
#include <cstring>
using namespace std;
typedef long long ll;

inline int read(void)//Read in
{
    register int x = 0;
    register short sgn = 1;
    register char c = getchar();
    while (c < 48 || 57 < c)
    {
        if (c == 45)
            sgn = 0;
        c = getchar();
    }
    while (47 < c && c < 58)
    {
        x = (x << 3) + (x << 1) + c - 48;
        c = getchar();
    }
    return sgn ? x : -x;
}
inline void write(ll x)//output
{
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

const int N = 40;
ll f[N];
unordered_map<ll, int> mp;

int main()
{
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    int n = read(), m = read();
    int res = 0x7fffffff;//result
    for (int i = 0; i < n; ++i)
        f[i] = (1ll << i);//Each point can reach itself
    for (int i = 1; i <= m; ++i)
    {
        int u = read(), v = read();
        --u, --v;//Binary starts from one bit, subtracts one from each number, and then performs or operation. If you don't understand, you can draw it on the paper and understand it
        f[u] |= (1ll << v);//u can reach v
        f[v] |= (1ll << u);//v can reach the point u
    }

    for (int i = 0; i < (1 << (n >> 1)); ++i)//The core of two-way search is to search the state of the first half and operate on the first half
    {
        ll t = 0;//Record temporary status
        int cnt = 0;//Record temporary results
        for (int j = 0; j < (n >> 1); ++j)
        {
            if ((i >> j) & 1)//If j is selected
            {
                t ^= f[j];//Just XOR
                ++cnt;
            }
        }
        if (!mp.count(t))//Last save hash table
            mp[t] = cnt;
        else
            mp[t] = min(mp[t], cnt);
    }
    for (int i = 0; i < (1 << (n - (n >> 1))); ++i)//Second half state
    {
        ll t = 0;
        int cnt = 0;
        for (int j = 0; j < (n - (n >> 1)); ++j)
        {
            if ((i >> j) & 1)
            {
                t ^= f[(n >> 1) + j];//Since it starts from 0, but this search is the second half, all (n > > 1) + J represent our operations on the remaining points
                ++cnt;
            }
        }
        if (mp.count(((1ll << n) - 1) ^ t))//If it can match exactly, update the minimum value
            res = min(res, cnt + mp[((1ll << n) - 1) ^ t]);
        }
    write(res);//Output answer
    return 0;
}

If you can't figure out what to do during the game, there are still ways. Refer to lo Gu Metaphysical algorithm of AuCloud

randomization
The first half is the same as the two-way search. An a array is added to record the serial number for random scrambling, once at a time, and then the sa() function traverses the operation points from front to back to determine whether the conditions are met

If it is satisfied, record the minimum value and exit. If it is not possible after traversal, return the maximum value and disrupt it again

This method is very metaphysical. It is A little similar to simulated annealing, but it does not have the essence of simulated annealing, but it is enough. It would be better if you can count A together and give points according to the test points in the competition

Look at the code comments

#include <iostream>
#include <algorithm>
#include <cstring>
#include <random>
#include <ctime>
using namespace std;
typedef long long ll;

inline int read(void)//Read in
{
    register int x = 0;
    register short sgn = 1;
    register char c = getchar();
    while (c < 48 || 57 < c)
    {
        if (c == 45)
            sgn = 0;
        c = getchar();
    }
    while (47 < c && c < 58)
    {
        x = (x << 3) + (x << 1) + c - 48;
        c = getchar();
    }
    return sgn ? x : -x;
}
inline void write(ll x)//output
{
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

const int N = 40;
ll f[N];
int a[N];
int n, m;

int sa()//Find the current result
{
    ll ans = 0;
    for (int i = 0; i < n; ++i)
    {
        ans ^= f[a[i]];
        if (ans == (1ll << n) - 1)//If you can operate so that all points are 1
            return i + 1;//i is from 0, all plus 1
    }
    return 0x7fffffff;
}

int main()
{
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    srand(time(NULL));//Random seed, it's up to him whether he can pass or not
    n = read(), m = read();
    int res = 0x7fffffff;
    for (int i = 0; i < n; ++i)
    {
        a[i] = i;//Record number for random disruption
        f[i] = (1ll << i);
    }
    for (int i = 1; i <= m; ++i)
    {
        int u = read(), v = read();
        --u, --v;
        f[u] |= (1ll << v);
        f[v] |= (1ll << u);
    }
    int qaq;
    if (n == 35)//Card time, no timeout
        qaq = 1000000;
    else
        qaq = 1900000;
    for (int i = 1; i <= qaq; ++i)
    {
        random_shuffle(a, a + n);//Random disorder order
        int qwq = sa();//Judge whether the current situation meets the conditions
        res = min(res, qwq);//Update minimum
    }
    write(res);
    return 0;
}

Of course, you found that you didn't get 100, maybe more than 80 points, or more than 90 points. After all, metaphysics depends on your seeds. It's not recommended here. Don't use it unless you have to
I've tried it many times. I really don't recommend it. If it's a test point, it's still very good

Keywords: C++ Algorithm

Added by sunil.23413 on Fri, 14 Jan 2022 15:02:10 +0200