Blue---枚举

P1003 NOIP 2011 提高组 铺地毯 - 洛谷

可以先根据题目意思画个图理解一下。本题很简单,就是找到覆盖在题目给的那个点最上边的地毯编号是多少,那我们就直接便利一遍所有的地毯,看看(x, y)最上边的地毯到底是哪个,注意在便利的时候应当从第n张地毯开始倒着便利,因为覆盖(x, y)的地毯不止一个,我们倒着便利第一次便利到就可以直接输出结果了。那怎么知道(x, y)有没有被地毯覆盖到呢?如下图给出答案。

cpp 复制代码
#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int n;
int a[N], b[N], g[N], k[N];

int main()
{
	cin >> n;
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i] >> b[i] >> g[i] >> k[i];
	}	
	
	int x = 0, y = 0;
	cin >> x >> y;
	
	int flag = 0;
	for(int i = n;i >= 1;i--)
	{
		int xm = a[i] + g[i];
		int ym = b[i] + k[i];
		if(x >= a[i] && x <= xm && y >= b[i] && y <= ym)
		{
			cout << i;	
			flag = 1;
			break;
		}	
	}
	if(!flag)
		cout << -1;
	
	return 0;
} 

P2010 NOIP 2016 普及组 回文日期 - 洛谷

思路1:根据题目给的两个日期,假设为x,y,枚举x~y的所有日期(包括x,y),逐一判断是否是回文日期,判断步骤就是先判断month和day是不是合法的,一年12个月,每个月的天数不能超过该月最大天数,润年和平年对于2月最大天数的影响。如果日期合法就进一步判断是否是回文日期。

cpp 复制代码
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

int date1, date2;
int cnt;

bool is_leap_year(int year)
{
	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

bool islegel(int date)
{
	int year = date / 10000;
	int month = date % 10000 / 100;
	int day = date % 100;
	static int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	
	//判断month是否合法
	if(month >= 13 || month <= 0)
		return false; 
	
	//看day是否合法 
	if(month == 2 && is_leap_year(year))
	{
		//润年有29天 
		return day >= 1 && day <= 29;
	}
	else
	{
		return day >= 1 && day <= days[month];
	}
}

bool isreverse(int date)
{
	string s = to_string(date);
	string sbackup = s;
	reverse(s.begin(), s.end());
	//string rev(s.rbegin(), s.rend());
	return sbackup == s;
}

int main()
{
	cin >> date1 >> date2;
	for(int i = date1;i <= date2;i++)
	{ 
		if(islegel(i) && isreverse(i))
		{
			++cnt;
		}
	}
	cout << cnt;
	
	return 0;
}

思路2:枚举年份,由对应的年份计算出对应该年份的回文日期,比如说当前枚举到的年份为2011,则其对应的回文日期为20111102。计算出当前枚举年份所对应的回文日期后,要判断month和day是否合法,如果合法再判断该回文日期是否在题给的起始date1和结束date2之间的一个日期范围。

cpp 复制代码
#include<iostream>
using namespace std;

int date1, date2;
int cnt;

//计算year对应的回文日期
int rev_year(int year)
{
	int x = year % 10;
	int y = year / 10 % 10;
	int z = year / 100 % 10;
	int k = year / 1000;
	return (year * 10000) + (x * 1000 + y * 100 + z * 10 + k);
} 

bool is_leap_year(int year)
{
	return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

bool islegel(int date)
{
	int year = date / 10000;
	int month = date % 10000 / 100;
	int day = date % 100;
	static int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	
	//判断month是否合法
	if(month >= 13 || month <= 0)
		return false; 
	
	//看day是否合法 
	if(month == 2 && is_leap_year(year))
	{
		//润年有29天 
		return day >= 1 && day <= 29;
	}
	else
	{
		return day <= days[month];
	}
}

int main()
{
	cin >> date1 >> date2;
	int d1y = date1 / 10000;
	int d2y = date2 / 10000;
	for(int i = d1y;i <= d2y;i++)
	{
		int date = rev_year(i);
		if(islegel(date) && date >= date1 && date <= date2)
		{
			++cnt;
		}
	}
	cout << cnt;
	
	return 0;
}

思路3:枚举所有的month和day,还原出对应的回文日期,思路基本上和思路2区别不大,还原出了回文日期之后不用判断是否合法,直接判断是否在date1, date2区间里就行。

cpp 复制代码
#include<iostream>
using namespace std;

int date1, date2;
int cnt;
int days[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

int rev_date(int month, int day)
{
	int x = day % 10;
	int y = day / 10;
	int z = month % 10;
	int k = month / 10;
	return (x * 1000 + y * 100 + z * 10 + k) * 10000 + (month * 100 + day);	
} 

int main()
{
	cin >> date1 >> date2;
	//这里还涉及到一个润年平年的问题,处理起来太麻烦了
	//但实际上就是0229对应的回文日期,为92200229
	//9220通过计算是润年,所以就不用单独判断是不是润年了,days[2]直接初始化成29 
	for(int i = 1;i <= 12;i++)
	{
		for(int j = 1;j <= days[i];j++)
		{
			int date = rev_date(i, j);
			if(date >= date1 && date <= date2)
				++cnt;
		}
	}
	cout << cnt;
	
	return 0;
}

P2327 SCOI2005 扫雷 - 洛谷

cpp 复制代码
#include<iostream>
#include<string.h>
using namespace std;

const int N = 1E4 + 10;
int y1[N], y2[N];
int n; 
int ret;

int check1()
{
	memset(y1, 0, sizeof(y1));
	y1[1] = 0;
	for(int i = 2;i <= n;i++)
	{
		y1[i] = y2[i - 1] - y1[i - 1] - y1[i - 2];	
		if(y1[i] != 1 && y1[i] != 0) 	
		{
			return 0;
		}
	}
	y1[n + 1] = y2[n] - y1[n] - y1[n - 1];
	if(y1[n + 1] == 0)
		return 1;
	else
		return 0;
}

int check2()
{
	memset(y1, 0, sizeof(y1));
	y1[1] = 1;
	//1下标已经确定,从2下标开始 
	for(int i = 2;i <= n;i++)
	{
		y1[i] = y2[i - 1] - y1[i - 1] - y1[i - 2];	
		if(y1[i] != 1 && y1[i] != 0) 	
		{
			return 0;
		}
	}
	y1[n + 1] = y2[n] - y1[n] - y1[n - 1];
	if(y1[n + 1] == 0)
		return 1;
	else
		return 0;
}

int main()
{
	cin >> n;
	for(int i = 1;i <= n;i++)
	{
		cin >> y2[i];
	}
	
	ret += check1();
	ret += check2();
	cout << ret;
	
	return 0;
}

78. 子集 - 力扣(LeetCode)

有些时候普通的枚举无法解决问题,此时可以用递归式枚举(DFS/BFS),也可以用二进制枚举。我们知道二进制是0/1,那就表示两种状态,所以当题目里有类似于选或者不选这种只有两种情况的方式就可以考虑二进制枚举。

cpp 复制代码
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> ret;
        //如果n==3,则1<<n就是8,则下边循环的就是[0, 7]
        for(int i = 0;i < (1 << n);i++)
        {
            vector<int> v;
            //st拷贝一份i,这样就不会影响到i++的下一次循环了
            int st = i;
            //看看st里有几个1,nums的大小为n的话,总共就n-1个下标
            //所以最多n-1个1,也就是全选,因此只要从0便利到n-1即可
            for(int j = 0;j < n;j++)
            {
                //如果结果为1说明st的第j位为1,表示选择
                //st的第j位就表示nums的下标j位
                if((st >> j) & 1)
                    v.push_back(nums[j]);
            }
            ret.push_back(v);
        }
        return ret;
    }
};
相关推荐
一木 之林2 小时前
第 8 节 C++ 设计模式在 AI 推理 SDK 和 Agent 工具运行时中如何落地
c++·人工智能·设计模式
神仙别闹2 小时前
基于 C++ 实现(控制台)考试报名系统
c++
weixin_307779133 小时前
C++代码实现MATLAB中的ode23t函数功能
开发语言·c++·算法·matlab
wuminyu3 小时前
Markword在紧凑对象头上的实现原理剖析
java·linux·c语言·jvm·c++
hansang_IR3 小时前
【代数与组合数学 | 那忘算 5】生成函数 & 例题 & 卷积
c++·算法·多项式·生成函数·母函数
无忧.芙桃3 小时前
数据结构之排序算法(上):从评价指标到插入、希尔、选择与堆排序
c语言·c++·排序算法
syagain_zsx3 小时前
算法基础篇 · 02 高精度(C++ 题解)
开发语言·c++·学习·高精度
6Hzlia3 小时前
【Classic 150 刷题计划】 LeetCode 242. 有效的字母异位词 | C++ 哈希计数与严密防线
c++·算法·leetcode
wabs6663 小时前
关于二叉树【力扣101.对称二叉树的思考】
数据结构·c++·算法·leetcode·二叉树