一.!一道好题!贪心+二分+dfs
P2329 [SCOI2005]栅栏
题目描述
农夫约翰打算建立一个栅栏将他的牧场给围起来,因此他需要一些特定规格的木材。于是农夫约翰到木材店购买木材。可是木材店老板说他这里只剩下少部分大规格的木板了。不过约翰可以购买这些木板,然后切割成他所需要的规格。而且约翰有一把神奇的锯子,用它来锯木板,不会产生任何损失,也就是说长度为10的木板可以切成长度为8和2的两个木板。
你的任务:给你约翰所需要的木板的规格,还有木材店老板能够给出的木材的规格,求约翰最多能够得到多少他所需要的木板。
输入格式
第一行为整数m(m<= 50)表示木材店老板可以提供多少块木材给约翰。紧跟着m行为老板提供的每一块木板的长度。
接下来一行(即第m+2行)为整数n(n <= 1000),表示约翰需要多少木材。
接下来n行表示他所需要的每一块木板的长度。木材的规格小于32767。(对于店老板提供的和约翰需要的每块木板,你只能使用一次)。
输出格式
只有一行,为约翰最多能够得到的符合条件的木板的个数。
输入:
4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30
输出:
7
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<cmath>
#include<deque>
#include<queue>
#define debug cout<<"ok"<<endl
typedef long long ll;
const int maxn=1e4+1;
const int mod=1e9+7;
using namespace std;
int sum,m,n,a[maxn],b[maxn],c[maxn],waste,mid,le,ri,ans;
bool dfs(int x,int last)
{
bool flag;
if(waste>sum-c[mid])return false;
if(x==0)return true;
for(int i=last;i<=m;i++)
{
if(a[i]>=b[x])
{
a[i]-=b[x];
if(a[i]<b[1])waste+=a[i];
if(b[x-1]==b[x])flag=dfs(x-1,i);
else flag=dfs(x-1,1);
if(a[i]<b[1])waste-=a[i];
a[i]+=b[x];
if(flag)return true;
}
}
return false;
}
int main()
{
cin>>m;
for(int i=1;i<=m;i++)
cin>>a[i],sum+=a[i];
cin>>n;
for(int i=1;i<=n;i++)
cin>>b[i];
sort(a+1,a+m+1);
sort(b+1,b+1+n);
c[0]=0;
for(int i=1;i<=n;i++)
c[i]=c[i-1]+b[i];
while(c[n]>sum)n--;
ri=n;
while(le<=ri)
{
waste=0;
mid=(le+ri)>>1;
if(dfs(mid,1))
ans=mid,le=mid+1;
else ri=mid-1;
}
cout<<ans<<endl;
return 0;
}
二.记忆化搜索
边界条件与递归方程是递归函数的两个要素。
递归构建有三个条件:1)参数;2)边界;3)范围。
据此来分析递归过程如何写。
1) 参数:明确参数的意义以及当前的值;
2) 边界:一个递归函数一定要有边界,而且边界一定要考虑全面,不能漏,否则它就可能死循环;
3) 范围:就是你在递归时的选择往哪儿走,也就是说,你的递归调用的函数返回值。
然后我们现在再来看一下记忆化搜索:
①定义好一个数组,用来存储递归所求出来的值;②在主程序里,memset一下,一般都是赋初值为-1,然后把这个数组的边界值设置好;③在递归函数里,首先加一句:if (这个数组的值>=0) return 这个值【如果赋初值为-1的话,一般都是>=0】;其次,在后面的递归调用中,先给这个数组赋值,再return。 (这一段话转自CSDN)
例题:P1434 [SHOI2002]滑雪
题目描述
Michael喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可行的滑坡为24-17-16-1(从24开始,在1结束)。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
思路:注意是问所经过的最长路径长度而不是高度,然后详情都在代码里
#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<cmath>
#define debug cout<<"ok"<<endl
typedef long long ll;
const int maxn=1e4+500;
const int mod=1e9+7;
using namespace std;
int n,m,ans,mp[maxn][maxn],f[maxn][maxn];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
inline int read()//快读,但感觉没有快多少~~
{
int f=0,x=0;
char ch=getchar();
while(ch>'9'||ch<'0'){f|=(ch=='-');ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return f?-x:x;
}
int dfs(int x,int y)
{
if(f[x][y])return f[x][y];//记忆化搜索如果之前搜过(不是0)那么直接返回省时间防TLE
f[x][y]=1;//就算只有一格那么它所能滑到的最大距离为1
for(int i=0;i<4;i++)
{
int nx=dx[i]+x;
int ny=dy[i]+y;
if(nx>0&&ny>0&&nx<=n&&ny<=m&&mp[nx][ny]<mp[x][y])//mp存的是高度,必须从高往下滑只要不越界且符合题意能滑到就滑
dfs(nx,ny),/*先搜搜完之后直接返回f[nx][ny]的值*/f[x][y]=max(f[x][y],f[nx][ny]+1);//然后类似动规,看是本身的值大还是从nx,ny走过来的值大
}
return f[x][y];
}
int main()
{
n=read();
m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
mp[i][j]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=max(ans,dfs(i,j));//取最大值
cout<<ans<<endl;
return 0;
}
三. 深搜染色
例题1:单词方阵
给一n×nn \times nn×n的字母方阵,内可能蕴含多个“yizhong”单词。单词在方阵中是沿着同一方向连续摆放的。摆放可沿着 888 个方向的任一方向,同一单词摆放时不再改变方向,单词与单词之间可以交叉,因此有可能共用字母。输出时,将不是单词的字母用*代替,以突出显示单词。例如:
输入:
8 输出:
qyizhong *yizhong
gydthkjy gy******
nwidghji n*i*****
orbzsfgz o**z****
hhgrhwth h***h***
zzzzzozo z****o**
iwdfrgng i*****n*
yyyygggg y******g
很典型的深搜题,向八个方向搜索,找到y后找i,然后保存方向,朝着这个方向去搜,单词全部搜到符合题意就用vis数组标记路径,未被标记的输出*即可。
注意
1.最好定义一个结构体数组来保存路径(x,y);
2.函数别忘了写return不然会re关键是codeblocks上能编译通过…;
3.按题目要求用s[];保存单词;
4.s[]是从0开始的所以step==6的时候单词搜索完毕继续+1再调用一次用于保存路径(这样才能路径完整)。
#include<bits/stdc++.h>
using namespace std;
int n,m,vis[105][105];
char mp[105][105],s[]="yizhong";
struct node
{
int x,y;
}way[105];
int dir[][2]={{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
void dfs(int sx,int sy,int di,int step)
{
if(step==7)
for(int i=0;i<7;i++)
vis[way[i].x][way[i].y]=1;
else {
int nx=sx+dir[di][0];
int ny=sy+dir[di][1];
if(mp[nx][ny]==s[step+1]||step==6)
{
way[step].x=sx;
way[step].y=sy;
dfs(nx,ny,di,step+1);
}
}
}
int main()
{
cin>>n;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>mp[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(mp[i][j]=='y')
{
for(int k=0;k<8;k++)
{
int x=i+dir[k][0];
int y=j+dir[k][1];
if(mp[x][y]=='i')
dfs(i,j,k,0);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(vis[i][j]==1)
cout<<mp[i][j];
else cout<<'*';
if(j==n)cout<<endl;
}
return 0;
}