POJ 3185 The Water Bowls 反转+点灯游戏

一、题目大意

有20盏灯(其实20盏灯数据量太少了,1e7我觉得差不多都可以过)放在同一行,点亮某一盏灯,它左右两边的灯也会亮,然后边缘特殊考虑,电亮两端的灯,只会照亮它相邻的那一个。

二、解题思路

这其实就是一个反转问题,我们从第二盏灯开始循环到第20盏灯,对于每一次循环的i,我们计算出i-1的灯是否亮着,如果i-1亮着,我们就continue,如果i-1不亮,那么电亮i,来照亮i-1。

然后计算每盏灯是不是亮着,可以记录一个数组fi,记录是否有 i-1,i+1的区间内被点亮,假如说一个位置i,fi==1,fi-1==1,那么相当于i位置与起始时的i位置情况相同,如果fi和fi-1中有一个为0,另一个为1的话,那么相当于i位置与起始时的情况相反。

这样我们用尺取法,每次维护i-1和i-2位置的fi的和,这样结合i位置的起始情况,就知道i位置是不是亮着的,然后生成fi的值,不断循环即可,循环结束之后,判断下最后一盏灯是不是亮着的,如果是的话,这是一个可行解,然后把fi数组求和,就是反转总次数。

因为我们是从第二盏灯开始考虑的,所以我们要循环判断下第一盏灯f1为1或0的两种情况,分别求解。

这种反转问题(点灯游戏),特点就是反转两次和没反转一样,然后都是牵一发而动全身,这种问题解题的关键就是找到可以唯一决定某个位置的条件,例如当fi,fi-1为定值时,fi+1的值可以直接决定i是不是亮着的,这样的话,每次通过i+1保证i,然后不断循环,最后对无法保证的区间(20,20)做判断,通过判断保存可行解,不通过则找下一种情况。

三、代码

cpp 复制代码
#include <iostream>
using namespace std;
int revCnt[27], ansCnt = 0x3f3f3f3f;
bool drinkable[27];
void input()
{
    int x;
    for (int i = 1; i <= 20; i++)
    {
        scanf("%d", &x);
        if (x == 0)
        {
            drinkable[i] = true;
        }
        else
        {
            drinkable[i] = false;
        }
    }
}
void flushRevCnt()
{
    for (int i = 1; i <= 20; i++)
    {
        revCnt[i] = 0;
    }
}
void saveAns()
{
    int currentAns = 0;
    for (int i = 1; i <= 20; i++)
    {
        currentAns += revCnt[i];
    }
    if (currentAns < ansCnt)
    {
        ansCnt = currentAns;
    }
}
void solve()
{
    int currentSum = revCnt[1];
    for (int i = 2; i <= 20; i++)
    {
        bool isDrinkable = drinkable[i - 1];
        if (currentSum % 2 != 0)
        {
            isDrinkable = !drinkable[i - 1];
        }
        if (!isDrinkable)
        {
            revCnt[i] = 1;
        }
        currentSum += revCnt[i];
        // 我们这个位置i,作为下次的判断条件,那么i-1,i,i+1可以作为比较的因素,i-1-1得去掉
        if (i - 1 - 1 >= 1)
        {
            currentSum -= revCnt[i - 1 - 1];
        }
    }
    int lastRevCnt = revCnt[19] + revCnt[20];
    bool isLastDrinkable = drinkable[20];
    if (lastRevCnt % 2 != 0)
    {
        isLastDrinkable = !drinkable[20];
    }
    if (isLastDrinkable)
    {
        saveAns();
    }
}
int main()
{
    input();
    flushRevCnt();
    solve();
    flushRevCnt();
    revCnt[1] = 1;
    solve();
    printf("%d\n", ansCnt);
    return 0;
}
相关推荐
圣光SG1 小时前
Java操作题练习(七)
java·开发语言·算法
Cx330_FCQ2 小时前
Tmux使用
服务器·git·算法
拳里剑气3 小时前
C++算法:多源BFS
c++·算法·宽度优先·多源bfs
ysa0510304 小时前
【板子】短序列dp(换成维护更小常数维度的dp)
c++·笔记·算法·板子
shwill1234 小时前
PID 算法(三)--- 增量 PID ↔ 单神经元 PID 等价映射
linux·算法
wabs6665 小时前
关于图论【卡码网110.字符串迁移的思考】
数据结构·算法·图论
hanlin036 小时前
刷题笔记:力扣第242、349题(哈希表)
笔记·算法·leetcode
浮沉9877 小时前
二分查找算法例题(二)
算法
only-qi7 小时前
大模型Agent面试攻略:落地工程痛点、评估体系与Agentic RAG核心精讲
人工智能·算法·面试·职场和发展·langchain·rag
Gauss松鼠会9 小时前
【GaussDB】GaussDB锁阻塞源头查询
java·开发语言·前端·数据库·算法·gaussdb·经验总结