USACO 6.3 章节 你对搜索和剪枝一无所知QAQ
emmm........很久很久以前 把6.2过了 所以emmmmmm 直接跳过 ,从6.1到6.3吧
Fence Rails
题目大意
N<=50
个数A1,A2...
1023
个数,每个数数值<=128
,B
问 A 们能拆分成多少个B,求最多的个数
样例 解释
A:
30=30
40=18+19+3
50=15+16+17+2
25=24
B:
15 (ok)
16 (ok)
17 (ok)
18 (ok)
19 (ok)
20
21
25
24 (ok)
30 (ok)
所以最多7个
题解
首先 如果对B 排序,假设最优个数为k个
显然 如果k个可行,那么排序后的B 的前k个可行
又 如果k个可行那么k-1个可行,综上又满足二分
先 sort+二分+从大到小放b (第4个点就超时了)
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "fence8";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
int n;
int a[100];
int la[100];
int suma;
int R;
int r[1100];
int sumr[1100];
int dfs(int idx){
per(i,0,n){
if(a[i] < r[idx]){
return false;
}
if(a[i] == a[i+1] && la[i] == la[i+1]){
continue;
}
if(la[i] < r[idx]){
continue;
}
if(idx == 0){
return true;
}
la[i] -= r[idx];
int ret = dfs(idx-1);
la[i] += r[idx];
if(ret){
return true;
}
}
return false;
}
bool test(int idx){
if(sumr[idx] > suma)return false;
return dfs(idx);
}
int main(){
usefile();
scanf("%d",&n);
rep(i,0,n){
scanf("%d",a+i);
}
rep(i,0,n){
la[i]=a[i];
suma += a[i];
}
sort(a,a+n);
scanf("%d",&R);
rep(i,0,R){
scanf("%d",r+i);
}
sort(r,r+R);
if(r[0] > a[n-1]){
cout<<0<<endl;
return 0;
}
sumr[0]=r[0];
rep(i,1,R){
sumr[i]=sumr[i-1]+r[i];
}
int l=0,r=R;
while(l+1<r){
int mid = (l+r)/2;
if(test(mid)){
l = mid;
}else{
r = mid;
}
}
cout<<l+1<<endl;
return 0;
}
对B 的枚举过程加了相同长度的枚举优化 tle 5
int dfs(int idx,int stn = n){
per(i,0,stn){
if(a[i] < r[idx]){
return false;
}
if(a[i] == a[i+1] && la[i] == la[i+1]){
continue;
}
if(la[i] < r[idx]){
continue;
}
if(idx == 0){
return true;
}
la[i] -= r[idx];
int ret;
if(r[idx] == r[idx+1]){
ret = dfs(idx-1,i+1);
}else{
ret = dfs(idx-1);
}
la[i] += r[idx];
if(ret){
return true;
}
}
return false;
}
增加了 无效残余木料 AC
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "fence8";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
int n;
int a[100];
int la[100];
int suma;
int R;
int r[1100];
int sumr[1100];
int dfs(int idx,int stn = n){
if(suma < sumr[idx]){
return false;
}
per(i,0,stn){
if(a[i] < r[idx]){
return false;
}
if(a[i] == a[i+1] && la[i] == la[i+1]){
continue;
}
if(la[i] < r[idx]){
continue;
}
if(idx == 0){
return true;
}
la[i] -= r[idx];
suma-=r[idx];
bool predel = la[i] < r[0];
if(predel){
suma -= la[i];
}
int ret;
if(idx > 0 && r[idx-1] == r[idx]){
ret = dfs(idx-1,i+1);
}else{
ret = dfs(idx-1);
}
if(predel){
suma += la[i];
}
suma+=r[idx];
la[i] += r[idx];
if(ret){
return true;
}
}
return false;
}
bool test(int idx){
if(sumr[idx] > suma)return false;
return dfs(idx);
}
int main(){
usefile();
scanf("%d",&n);
rep(i,0,n){
scanf("%d",a+i);
}
rep(i,0,n){
la[i]=a[i];
suma += a[i];
}
sort(a,a+n);
scanf("%d",&R);
rep(i,0,R){
scanf("%d",r+i);
}
sort(r,r+R);
if(r[0] > a[n-1]){
cout<<0<<endl;
return 0;
}
sumr[0]=r[0];
rep(i,1,R){
sumr[i]=sumr[i-1]+r[i];
}
int l=0,r=R;
while(l+1<r){
int mid = (l+r)/2;
if(test(mid)){
l = mid;
}else{
r = mid;
}
}
cout<<l+1<<endl;
return 0;
}
综上 二分+暴搜+减枝+处理顺序贪心
Cryptcowgraphy
题目大意
一个字符串能否通过 正数次操作使得变为
Begin the Escape execution at the Break of Dawn
一次操作: 选取 ...C...O...W...
,把C->O
的字符串和O->W
的字符串交换,然后去掉这三个选中C
,O
,W
题解
显然 特判打表
我们已经知道 目标串 和 起始串
所以如果可行,那么 个数关系有C=O=W =(len(起始串)-len(目标串))/3
,所以预先筛选的一个优化是,统计各个字符的个数,和目标串进行比较
所以 当比较是可能
时,答案要么0 0,要么 1 字母C 的个数
我们可以优化的点
- 字母个数统计
- 被C O W 分割的在任意时刻是 目标串的子串
- 搜索顺序先O
- 字符串hash [注意 这个方法 如果你是在打cf,那么很可能被hack
注意输入是一行....所以不要scanf %s
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "cryptcow";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
const char Goal[] = "Begin the Escape execution at the Break of Dawn";
const int Mod = 999983;
char s[110];
int ans, cnt;
bool hsh[Mod];
// 删除a b c位置上的, 交换a->b b->c
void work(int a, int b, int c) {
static char tmp[100];
int len = strlen(s), tot = 0;
for(int i = 0; i < a; ++i) {
tmp[tot++] = s[i];
}
for(int i = b + 1; i < c; ++i) {
tmp[tot++] = s[i];
}
for(int i = a + 1; i < b; ++i) {
tmp[tot++] = s[i];
}
for(int i = c + 1; i < len; ++i) {
tmp[tot++] = s[i];
}
tmp[tot] = 0;
strcpy(s, tmp);
}
int getHash() {
int ret = 0, len = strlen(s);
for(int i = 0; i < len; ++i) {
int num = (s[i]==' ')?1:(isupper(s[i]) ? s[i] - 'A' + 2 : s[i] - 'a' + 28);
ret = (ret * 57 + num) % Mod;
}
return ret;
}
bool dfs(int depth) {
if(strcmp(s, Goal) == 0) {
ans = depth;
return true;
}
int x = getHash();
if(hsh[x]) {
return false;
}
hsh[x] = true;
++cnt;
// 被C O W 分割的 字串应该是Goal的连续子串
static char sub[100];
int len = strlen(s);
int c[20], o[20], w[20];
c[0] = o[0] = w[0] = 0;
for(int i = 0, j = 0; i < len; ++i) {
if(s[i] == 'C' || s[i] == 'O' || s[i] == 'W') {
if(s[i] == 'C') {
c[++c[0]] = i;
}
if(s[i] == 'O') {
o[++o[0]] = i;
}
if(s[i] == 'W') {
w[++w[0]] = i;
}
sub[j] = 0;
if(!strstr(Goal, sub)) { //
return false;
}
j = 0;
}
else {
sub[j++] = s[i];
}
}
// C = W = O
if(o[0] != c[0] || o[0] != w[0] || w[0] != c[0]) {
return false;
}
char pre[100];
strcpy(pre, s); // 递归暂存
// 查找顺序 先找O
rep(j,1,o[0]+1){
per(k,1,w[0]+1){
if(w[k] < o[j])break;
rep(i,1,c[0]+1){
if(c[i] > o[j])break;
work(c[i], o[j], w[k]);
bool ret = dfs(depth + 1);
if(ret){
return true;
}
if(cnt > 200000) { // ............................
return false;
}
strcpy(s, pre);
}
}
}
return false;
}
int main() {
usefile();
cin.getline(s,100);
// scanf("%s",s);
int ret = dfs(0);
printf("%d %d\n", ret, ans);
return 0;
}
Cowcycles
题目大意
25 <= F1 < F2 <= 80
在[F1,F2]
范围找[1,5]
个数f1,f2..
5 <= R1 < R2 <= 40
在[R1,R2]
范围找[1,10]
个数r1,r2..
ratio(i,j) = fi/rj
在max ratio/min ratio >= 3
的限制下
把所有ratio(i,j)
排序,最小化 排序后数组相邻值 的差 的方差
求具体方案
题解
ratio(i1,j1)/ratio(i2,j2) = i1*j2/i2*j1
要使这个值的最大值大于3,注意到都是正数,也就是(max(i)*max(j))/(min(i)*min(j)) >= 3
然后因为ratio要先sort,再最小化 差 的方差,就感觉 无路可推,只能暴搜
优化
- 默认的限制减枝
- 个数少,运算过程相对有序 --> 计算顺序+插入排序
- 搜一搜初中的方差变形公式,省掉最外层的
1/n
有只用比较sum{平方}+(sum{}平方)/n
注意 以下代码过了USACO 但是有潜在风险,浮点数比大小!!
如果是打cf,可能会被叉
#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
using namespace std;
const string filename = "cowcycle";
void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
}
int s1[20],s2[20];
int ans1[20],ans2[20];
int F,F1,F2;
int R,R1,R2;
int cnt;
double rate[100],diff[100];
double minvf=10000000;
void count(){
int k=0;
double sum=0,avg,vf=0,sumf=0,p;
// 数据量小 采用插入排序
rep(i,0,F){
rep(j,0,R){
p=(double)s1[i]/s2[j];
int l=++k;
while (rate[l-1]>=p) {
rate[l]=rate[l-1];
l--;
}
rate[l]=p;
}
}
rep(i,1,cnt){
diff[i]=rate[i+1]-rate[i];
sum+=diff[i];
sumf+=diff[i]*diff[i];
}
avg=sum/(cnt-1);// 相邻值的差的个数 比值的个数少1
vf=sumf-sum*avg;
if (vf<minvf) {
minvf=vf;
memcpy(ans1,s1,sizeof(int)*F);
memcpy(ans2,s2,sizeof(int)*R);
}
}
// 枚举后齿轮 从w 到R2-R+k+1
void sc2(int k,int w){
if (k==R){
if (s1[F-1]*s2[R-1]<3*s1[0]*s2[0]) // 题目限制条件剪枝
return;
count();
return;
}
rep(i,w,R2-R+k+2){
s2[k]=i;
sc2(k+1,i+1);
}
}
// 枚举前齿轮 从w到F2-F+k+1
void sc1(int k,int w){
if (k==F) {
sc2(0,R1);
return;
}
rep(i,w,F2-F+k+2){
s1[k]=i;
sc1(k+1,i+1);
}
}
int main() {
usefile();
cin>> F >> R >> F1 >> F2 >> R1 >> R2;
cnt=F*R;
sc1(0,F1);
rep(i,0,F){
cout << ans1[i] << " \n"[i==F-1];
}
rep(i,0,R){
cout << ans2[i] << " \n"[i==R-1];
}
return 0;
}
总结
搜索+剪枝,剪究竟要怎么剪
引用一个大佬的话https://apps.topcoder.com/forums/?module=Thread&threadID=669047&start=0&mc=6#1216077
Well, if the optimizations change the complexity of the solution asymptotically, you can quite sure.
Otherwise you can't depend on anything, I think.
重要的是 找到 能明确改变算法 复杂度的剪枝。
反过来分析
第1题
剩余 体积 处理,应该能优化了搜索树的叶节点个数,十分关键
重复体积的搜索处理,优化了枚举体积的次数,对相同体积有多个的情况,从 可放空间数
的相同个数
次方,优化到了???,不知道怎么表示,但是 大量减少了重复枚举是肯定的
从大到小尝试,优化了末端个数?(吗)
第2题
对于错误的预处理 直接一边就否定掉了
目标串的子串是一个有效的大优化,当 在 去掉COW 以后,连接出了 不应该的字符串,可以立刻剪掉,对搜索空间优化大
搜索顺序 先O 还好 大概是常数倍数优化
字符串hash,除了上面的方法,还可以 用其它的 比如神奇的偏移+异或字符串等等, 优化的是很大的字符串比较代价,常数倍数
第3题
基本没有剪枝[题目限制的剪枝是当然
主要靠的是算法使用的性能优化
优化 排序[数量少的时候插入,在具体工程实践中,数量较少的时候 也同样会采取 数组取代map 进行遍历,冒泡取代其它排序 ]
优化 方差运算,目前这样公式变化
默认 n-1
次加法 1次除法,算平均数,n
次减法,n
次乘法,n-1
次加法得到结果
优化后 2(n-1)
次加法 1次除法,1
次减法n+1
次乘法,得到结果
假设所有 运算类型时间代价相同,那么算是优化掉了约1/4
时间[然而一般来说减法的速度是比乘除快很多,再加上CPU的指令pipeline运算优化,可能影响有,但不大
本文其它博客链接
牛客:https://blog.nowcoder.net/n/2773db6ff811467a922070d9a5c64a39
github:https://yexiaorain.github.io/Blog/2019-07-01-USACO-6.3/
USACO 6.3 章节 你对搜索和剪枝一无所知QAQ的更多相关文章
- 搜索(剪枝优化):HDU 5113 Black And White
Description In mathematics, the four color theorem, or the four color map theorem, states that, give ...
- [CF293B]Distinct Paths_搜索_剪枝
Distinct Paths 题目链接:http://codeforces.com/problemset/problem/293/B 数据范围:略. 题解: 带搜索的剪枝.... 想不到吧..... ...
- ICPC Asia Nanning 2017 I. Rake It In (DFS+贪心 或 对抗搜索+Alpha-Beta剪枝)
题目链接:Rake It In 比赛链接:ICPC Asia Nanning 2017 Description The designers have come up with a new simple ...
- USACO 1.3... 虫洞 解题报告(搜索+强大剪枝+模拟)
这题可真是又让我找到了八数码的感觉...哈哈. 首先,第一次见题,没有思路,第二次看题,感觉是搜索,就这样写下来了. 这题我几乎是一个点一个点改对的(至于为什么是这样,后面给你看一个神奇的东西),让我 ...
- USACO 6.4 章节
The Primes 题目大意 5*5矩阵,给定左上角 要所有行,列,从左向右看对角线为质数,没有前导零,且这些质数数位和相等(题目给和) 按字典序输出所有方案... 题解 看上去就是个 无脑暴搜 题 ...
- USACO 5.3 章节
相关讲解可在USACO上看原文,也可以搜索nocow找到翻译的! (nocow上有些微翻译是有问题的,如果想看nocow翻译的建议也对着英文看) 以下记录以下 自己之前未掌握的一些要点,以及按自己的括 ...
- POJ 1176 Party Lamps&& USACO 2.2 派对灯(搜索)
题目地址 http://poj.org/problem?id=1176 题目描述 在IOI98的节日宴会上,我们有N(10<=N<=100)盏彩色灯,他们分别从1到N被标上号码. 这些灯都 ...
- Usaco Training [2.1] The Castle 搜索
传送门 题目的输出的4个信息 前两个很容易,dfs,bfs都可以,图怎么建都可以 后两个在搜索的时候记录belong[i][j]和已有的size即可 代码应该比不少题解清晰吧 #include < ...
- USACO 5.5 章节
Picture 题目大意 IOI 1998 求n (<=5000)个矩形 覆盖的图形 的周长(包括洞), 坐标范围[-10000,10000] 题解 一眼离散化+2维线段树,但仔细一想 空间不太 ...
随机推荐
- 【题解】Hankson 的趣味题
题目大意 已知正整数$a_{0}$.$a_{1}$.$b_{0}$.$b_{1}$($1 \leq a_{0}, a_{1}, b_{0}, b_{1} \leq 2 \times 10^{9}$), ...
- Day4 --- Python中的控制结构(if, for, while, try...expect..)
if for while 可以参考 : https://www.cnblogs.com/HYLering/p/10051906.html try 是异常处理里面的 可以参考 : https://ww ...
- redis缓存架构-03-redis下的replication以及master+slave
1.master和slave的读写分离(水平扩容支持读高并发) 2.master主从复制流程 master开始复制给slave前的认证流程 master向slave复制流程 2.1 无磁盘化复制配置 ...
- Linux之systemd服务配置及自动重启
layout: post title: Linux之systemd服务配置及自动重启 date: 2019-09-09 tags: linux --- Linux之systemd服务配置及自动重启 0 ...
- JavaFX程序初次运行创建数据库并执行建表SQL
在我的第一个JavaFX程序完成安装的时候才突然发现,不能要用这个软件还要手动执行Sql来建表吧? 于是我的想法是在Main程序中执行时检测数据库连接状况,如果没有检测到数据库或者连接异常,那么出现错 ...
- 上传.cgi在252板子上跑
1.windows下写好.c程序 2.进入linux,准备交叉编译 arm-hisiv600-linux-gcc -g -Wall -o xxx.cgi xxx.c(交叉编译工具取决于板子) 3.把. ...
- 开源安全:PE分析
https://github.com/JusticeRage/Manalyze.git https://github.com/JusticeRage/Manalyze https://www.free ...
- CS184.1X 计算机图形学导论(第五讲)
一.观察:正交投影 1.特性:保持平行线在投影后仍然是平行的 2.一个长方体,对处在只有深度不同的位置上的同一物体来说,它的大小不会改变. 3.透视投影:平行线在远处会相交(例如铁轨) 4.glOrt ...
- HTML基础:<a>标签 编写个人收藏夹
编写个人收藏夹 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <t ...
- day18 python模块 random time sys os模块
day18 python 一.random模块 取随机整数 import random print(random.randint(1,2)) #顾头顾尾 p ...