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;
}
};