title: 专题4 - 搜索剪枝
tags: [搜索, 剪枝]
categories: [OI, 算法专题]
这种题没什么好说的,写就完了!
习题
P5440 【XR-2】奇迹
原题 · 标签:#搜索
考点
逐位枚举/预处理思想
题意
给定T组8位残缺日期字符串(格式:YYYYMMDD,-表示未知位,数字为固定位),求合法日期的数量,合法条件:
- 日期真实存在(年19999,月份112,日期符合对应月份/闰年规则);
- 三个数均为质数:
- 日(DD,2位)
- 月+日(MMDD,4位)
- 完整日期(YYYYMMDD,8位)
- 填充
-为数字后满足上述所有条件,统计合法方案数。
完整数据范围
- 测试组数:\(1 \le T \le 10\)
- 日期固定格式:8位字符串,仅包含数字0~9和字符
- - 年份范围:\(1 \le 年 \le 9999\)
- 残缺未知位数量 \(c\):\(0 \le c \le 8\)
思路
我写的比较麻烦……
主要思路是逐位枚举dfs,在最后统一判素数和日期合法性。
由于我没有预处理全部日期,而是对每一次询问都重新计算,所以需要特判--------。
在下面贴一下我的代码和xht的代码。
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define f(i,a,b) for(int i=(a);i<=(b);i++)
#define ll long long
#define PII pair<int,int>
#define DEBUG 1
#if DEBUG
#define d(x) cout << #x << " = " << x << endl;
#define da(a,l,r) cout << #a << "[" << l << "-" << r << "]:"; rep(i,l,r) cout << a[i] << " "; cout << endl;
#else
#define d(x)
#define da(a,l,r)
#endif
int day[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int prePrime[10010];
bool isPrime(int x){
if (x < 2) return false;
for (int i=2;i*i<=x;i++){
if (x % i == 0) return false;
}
return true;
}
int t;
string s;
void init(){
f(i,1,9999) if (isPrime(i)) prePrime[i] = 1;
}
int getnum(int l,int r){
int res = 0;
f(i,0,s.size()-1){
if(l <= i+1 && i+1 <= r){
res *= 10;
res += (s[i] - '0');
}
}
return res;
}
bool isDate(){
int year = getnum(1,4);
int month = getnum(5,6);
int dy = getnum(7,8);
if (month > 12 || dy > 31 || year == 0 || month == 0 || dy == 0) return false;
if (((year %4 == 0 && year % 100 != 0) || (year % 400 == 0))&&dy <= 29) return true;
if (day[month] >= dy) return true;
return false;
}
int ans;
string ss;
void dfs(int dep){
if (dep > 8){
if (!isDate()) return;
if (prePrime[getnum(5,8)] && prePrime[getnum(7,8)] && isPrime(getnum(1,8))) ans++;
return;
}
if (ss[dep-1] == '-'){
if ((dep >= 1 && dep <= 4)){
f(i,0,9){
s[dep-1] = i + '0';
dfs(dep + 1);
}
}
if (dep == 8){
if (s[6] == '3'){
f(i,0,1){
s[dep-1] = i + '0';
dfs(9);
}
}
else{
f(i,0,9){
s[dep-1] = i + '0';
dfs(dep + 1);
}
}
}
if (dep == 6){
if (s[4] != '1'){
f(i,0,9){
s[dep-1] = i + '0';
dfs(dep + 1);
}
}
else{
f(i,0,2){
s[dep-1] = i + '0';
dfs(dep + 1);
}
}
}
if (dep == 5){
f(i,0,1){
s[dep-1] = i + '0';
dfs(6);
}
}
if (dep == 7){
f(i,0,3){
s[dep-1] = i + '0';
dfs(8);
}
}
}
else {
dfs(dep+1);
}
}
signed main() {
init();
cin >> t;
while (t--){
cin >> s;
if (s == "--------"){
cout << 55157 << "\n";
continue;
}
ss = s;
ans = 0;
dfs(1);
cout << ans << "\n";
}
return 0;
}
B3624 猫粮规划
原题 · 标签:#搜索
考点
简单剪枝
题意
给定 \(n\) 份食物及每份食物的能量值,选取若干食物构成子集,统计子集能量总和落在区间 \([l, r]\) 内的子集总数量。
对于 \(50\%\) 的数据,满足 \(n\leq 20\)。
对于 \(100\%\) 的数据,满足 \(n\leq 40, 20\leq w[i] \leq 100, l\leq r \leq 300\)。
思路
如果只看\(n\)的数据范围,会觉得这是个折半搜索。
实际上本题并不需要写折半搜索,因为\(w_i\)最小为20,\(r\)最大为\(300\),所以最多选\(15\)次就会超过限制,加个剪枝就可以了。
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define f(i,a,b) for(int i=(a);i<=(b);i++)
#define ll long long
#define PII pair<int,int>
#define DEBUG 1
#if DEBUG
#define d(x) cout << #x << " = " << x << endl;
#define da(a,l,r) cout << #a << "[" << l << "-" << r << "]:"; rep(i,l,r) cout << a[i] << " "; cout << endl;
#else
#define d(x)
#define da(a,l,r)
#endif
const int N = 41;
int n,l,r;
int a[N];
int ans;
void dfs(int dep,int sum){
if (dep == n+1){
if (sum >= l && sum <= r) ans++;
return;
}
if (sum > r) return;
dfs(dep+1,sum+a[dep]);
dfs(dep+1,sum);
}
signed main() {
cin >> n >> l >> r;
f(i,1,n) cin >> a[i];
dfs(1,0);
cout << ans;
return 0;
}
P1092 NOIP 2004 提高组 虫食算
原题 · 标签:#搜索
考点
有难度的搜索
题意
给定一个 n 进制加法算式,由两个加数字符串和一个和字符串组成,算式中的每一位都用大写字母表示: 1. 相同字母代表相同数字,不同字母代表不同数字; 2. 恰好使用前 n 个大写字母(A~第n个字母),分别对应 0 ~ n-1 的不重复数字; 3. 三个字符串均为 n 位,允许前导 0,满足 加数1 + 加数2 = 和(n 进制加法规则,包含进位); 4. 题目保证有且仅有一组解,要求输出 A、B、C……依次对应的数字。
数据范围
- 进制数 n:1 ≤ n ≤ 26 - 三个字符串长度:均为 n 位 - 字符范围:仅使用前 n 个大写英文字母,每个字母至少出现一次 - 数据保证:唯一解,无多解、无解情况
思路
很久之前写的搜索,回想一下思路。
首先有进位,顺着不好考虑。所以我们从低位到高位写。
每当填完三个数,看看进位上是不是有问题。
枚举数从大到小枚举,这样剪枝能剪多一点。
最后再check一下正确性。
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define ll long long
void dnums(int a[],int s,int e){
rep(i,s,e) printf("%d ",a[i]);
printf("\n");
}
void filefreopen(string filename){
string infile = filename + ".in";
string outfile = filename + ".out";
freopen(infile.c_str(),"r",stdin);
freopen(outfile.c_str(),"w",stdout);
}
const int N = 30;
int ques[N][4];
int ans[N];
int vis[N];
int n;
bool judge1(){
int flag = 0;
rep(i,1,n){
int a = ques[i][1];
int b = ques[i][2];
int c = ques[i][3];
// 如果都已知
if (ans[a] != -1 && ans[b] != -1 && ans[c] != -1){
if ((ans[a] + ans[b] + flag) % n != ans[c]){
return false;
}
flag = (ans[a] + ans[b] + flag) / n;
}
else{
return true;
}
}
return (flag == 0);
}
bool judge(){
rep(i,1,n){
int a = ques[i][1];
int b = ques[i][2];
int c = ques[i][3];
// 如果都已知
if (ans[a] != -1 && ans[b] != -1 && ans[c] != -1){
if ((ans[a] + ans[b] + 1) % n != ans[c] && (ans[a] + ans[b]) % n != ans[c]){
return false;
}
}
else{
continue;
}
}
return true;
}
void dfs(int dep,int st){
// printf("dep : %d st : %d\n",dep,st);
// dnums(ans,0,n-1);
if (dep > n){
if (judge1()) {
rep(i,0,n-1) {cout << ans[i] << " ";}
exit(0);
}
return;
}
int qNum = ques[dep][st];
if (ans[qNum] != -1){
if (st == 3) dfs(dep+1,1);
else dfs(dep,st+1);
}
else{
for(int i=n-1;i>=0;i--){
if (vis[i]) continue;
vis[i] = 1;
ans[qNum] = i;
if (judge()){
if (st == 3) dfs(dep+1,1);
else dfs(dep,st+1);
}
ans[qNum] = -1;
vis[i] = 0;
}
}
}
signed main(){
cin >> n;
rep(i,1,3){
rep(j,1,n){
char c;
cin >> c;
ques[n-j+1][i] = c-'A';
}
}
rep(i,0,n-1) ans[i] = -1;
dfs(1,1);
return 0;
}
浙公网安备 33010602011771号