P2392 kkksc03考前临时抱佛脚
思路
- 由于只能一科一科的复习,所以其实就是找出一个算法能够把n个数分成两堆,使得这两堆的差值最小
- 贪心的想法是不行的
- 用搜索做,实质就是枚举。每次决定选还是不选这个数,一旦所选的数总和超过sum/2就返回
实现
#include<bits/stdc++.h>
using namespace std;
int sum;
int a[4][21];
int len[4];
int total[4];
int min_;
void dfs(int x,int y,int s)
{
if(y==len[x]-1 || s>=(total[x]/2.0))
{
if(max(s,total[x]-s)<min_)
min_ = max(s,total[x]-s);
return ;
}
dfs(x,y+1,s+a[x][y]);
dfs(x,y+1,s);
}
int ans;
int main()
{
for(int i=0;i<4;i++)
{
cin>>len[i];
}
for(int i=0;i<4;i++)
{
for(int j=0;j<len[i];j++)
{
cin>>a[i][j];
total[i]+=a[i][j];
}
min_ = 2000000;
dfs(i,0,0);
ans+=min_;
}
cout<<ans;
return 0;
}
P2036 [COCI2008-2009#2] PERKET
思路
dfs,注意初始条件应该是酸度为1,苦度为0。注意判断什么配料都没有加的情况。
实现
#include<bits/stdc++.h>
using namespace std;
int a[11];
int b[11];
int n;
int min_ = 10e9;
void dfs(int x,int y,int i)
{
if(i==n)
{
if(x==1 && y==0)
return ;
if(abs(x-y)<min_)
min_ = abs(x-y);
return ;
}
dfs(x*a[i],y+b[i],i+1);
dfs(x,y,i+1);
}
int ans;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%d%d",&a[i],&b[i]);
}
dfs(1,0,0);
cout<<min_;
return 0;
}
P1101 单词方阵
思路
用dfs有一些不便之处,因为它不仅要求相邻,还要沿着同一方向连续摆放,dfs就必须多一个参数。不如搜索遍全方阵,找到一个’y’就停下来,把八个方向都试一次。
实现
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
const int dx[] = {1, 1, 1, 0, 0, -1,-1,-1 };
const int dy[] = {1, 0,-1, 1,-1, 0 , 1,-1 };
const string cmp = "yizhong";
char A[maxn][maxn], ans[maxn][maxn];
int mark[maxn][maxn], n;
void dfs(int x,int y) {
for(int i = 0;i < 8;i++) {
int flag = 1;
for(int j = 1;j <= 6;j++) {
int nx = x + j*dx[i];
int ny = y + j*dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > n) {
flag = 0;
break;
}
if(cmp[j] != A[nx][ny]) {
flag = 0;
break;
}
}
if(flag == 0) continue;
for(int j = 0;j <= 6;j++) {
int nx = x + j*dx[i];
int ny = y + j*dy[i];
ans[nx][ny] = A[nx][ny];
}
}
return;
}
int main() {
cin >> n;
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= n;j++) {
cin >> A[i][j];
}
}
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= n;j++) {
if(A[i][j] == 'y') dfs(i,j);
}
}
for(int i = 1;i <= n;i++) {
for(int j = 1;j <= n;j++) {
if(ans[i][j] == 0) ans[i][j] = '*';
cout << ans[i][j];
}
cout << endl;
}
return 0;
}
P2404 自然数的拆分问题
思路
用回溯做,sum表示当前已选数之和,a表示上一个数。那这次选的时候必须要大于等于a才能不重复。
实现
#include<bits/stdc++.h>
using namespace std;
int n;
void dfs(int sum,int a,vector<int> &ans) {
if(sum>n) return ;
else if(sum==n)
{
cout<<ans[0];
for(int i=1;i<ans.size();i++)
cout<<"+"<<ans[i];
cout<<endl;
}
for(int i=a;i<n;i++)
{
ans.push_back(i);
dfs(sum+i,i,ans);
ans.pop_back();
}
}
int main() {
cin>>n;
vector<int> ans;
dfs(0,1,ans);
return 0;
}
P1596 [USACO10OCT]Lake Counting S
思路
用dfs把所有经过的’w’改为’.',数有多少个连通块即可
实现
#include<bits/stdc++.h>
using namespace std;
int m,n;
int ans;
char p[110][110];
void dfs(int x,int y) {
if(x<0 || x>=m || y<0 || y>=n || p[x][y]=='.') return ;
p[x][y] = '.';
dfs(x+1,y);
dfs(x-1,y);
dfs(x,y+1);
dfs(x,y-1);
dfs(x+1,y+1);
dfs(x+1,y-1);
dfs(x-1,y+1);
dfs(x-1,y-1);
}
int main() {
cin>>m>>n;
for(int i=0;i<m;i++)
{
scanf("%s",p[i]);
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(p[i][j]=='W')
{
dfs(i,j);
ans++;
}
}
}
cout<<ans;
}
P1162 填涂颜色
思路
读入数据的时候将1修改为-1,dfs将外面所有的0修改为1,输出时分类即可
实现
#include<bits/stdc++.h>
using namespace std;
int n;
int ans;
int p[32][32];
void dfs(int x,int y) {
if(x<0 || x>=n || y<0 ||y>=n || p[x][y]!=0) return ;
p[x][y] = 1;
dfs(x+1,y);
dfs(x-1,y);
dfs(x,y+1);
dfs(x,y-1);
// dfs(x+1,y+1);
// dfs(x+1,y-1);
// dfs(x-1,y+1);
// dfs(x-1,y-1);
}
int main() {
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
scanf("%d",&p[i][j]);
if(p[i][j]==1) p[i][j] = -1;
}
}
for(int i=0;i<n;i++)
{
if(p[i][0]==0)
{
dfs(i,0);
}
if(p[i][n-1]==0)
{
dfs(i,n-1);
}
}
for(int i=0;i<n;i++)
{
if(p[0][i]==0)
{
dfs(0,i);
}
if(p[n-1][i]==0)
{
dfs(n-1,i);
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(p[i][j] == 1) cout<<"0 ";
else if(p[i][j] == -1) cout<<"1 ";
else cout<<"2 ";
}
cout<<endl;
}
}
P1443 马的遍历
思路
bfs,每个点上的值就是第一次经过该点时bfs的深度
实现
#include<bits/stdc++.h>
using namespace std;
const int dx[8]={-1,-2,-2,-1,1,2,2,1};
const int dy[8]={2,1,-1,-2,2,1,-1,-2};
queue<pair<int,int> > q;
int f[500][500];
bool vis[500][500];
int main()
{
int n,m,x,y;
memset(f,-1,sizeof(f));memset(vis,false,sizeof(vis));
cin>>n>>m>>x>>y;
f[x][y]=0;vis[x][y]=true;q.push(make_pair(x,y));
while(!q.empty())
{
int xx=q.front().first,yy=q.front().second;q.pop();
for(int i=0;i<8;i++)
{
int u=xx+dx[i],v=yy+dy[i];
if(u<1||u>n||v<1||v>m||vis[u][v]) continue;
vis[u][v]=true;
q.push(make_pair(u,v));
f[u][v]=f[xx][yy]+1;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
printf("%-5d",f[i][j]);
printf("\n");
}
return 0;
}
P1135 奇怪的电梯
思路
bfs 和上题一样
实现
#include<bits/stdc++.h>
using namespace std;
#include<bits/stdc++.h>
using namespace std;
const int dx[8]={-1,-2,-2,-1,1,2,2,1};
const int dy[8]={2,1,-1,-2,2,1,-1,-2};
queue<pair<int,int> > q;
int f[210];
bool vis[210];
int main()
{
int n,x,y;
int step = 0;
memset(vis,false,sizeof(vis));
cin>>n>>x>>y;
for(int i=1;i<=n;i++) cin>>f[i];
vis[x]=true;
q.push(make_pair(x,0));
int xx;
while(!q.empty())
{
xx=q.front().first;
step = q.front().second;
q.pop();
if(xx==y) break;
int u=xx+f[xx];
if(u>=1 && u<=n && !vis[u])
{
vis[u] = true;
q.push(make_pair(u,step+1));
}
u=xx-f[xx];
if(u>=1 && u<=n && !vis[u])
{
vis[u] = true;
q.push(make_pair(u,step+1));
}
}
if(xx==y)
cout<<step;
else
cout<<"-1";
return 0;
}
P2895 [USACO08FEB]Meteor Shower S
思路
首先用f数组记录下每块地化为焦土的时间(安全的地方为-1):
- 一开始把所有的地的时间都设置为-1
- 读入数据x,y,time,若 f [ x ] [ y ] = = − 1 f[x][y]==-1 f[x][y]==−1或 f [ x ] [ y ] > t i m e f[x][y]>time f[x][y]>time则更新 f [ x ] [ y ] f[x][y] f[x][y]
- 向上下左右四个方向,以同样的判断方法更新紧挨的四块地
然后浅用一个bfs,队列里存储x,y,time,表示在time时刻到达了 ( x , y ) (x,y) (x,y)处。每次向外扩散时仅当 f [ u ] [ v ] > t i m e + 1 f[u][v]>time+1 f[u][v]>time+1且 ( u , v ) (u,v) (u,v)未被访问过。bfs直到队列空或者找到一个 f [ x ] [ y ] = = − 1 f[x][y]==-1 f[x][y]==−1。
实现
#include<bits/stdc++.h>
using namespace std;
#include<bits/stdc++.h>
using namespace std;
const int dx[4]={-1,1,0,0};
const int dy[4]={0,0,1,-1};
int f[500][500];
bool vis[500][500];
typedef struct
{
int xx;
int yy;
int time;
}node;
queue<node> q;
int main()
{
int n;
cin>>n;
memset(f,-1,sizeof(f)); //开始时全部初始化为-1
memset(vis,false,sizeof(vis));
int time,x,y,u,v;
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&x,&y,&time);
if(f[x][y]>time || f[x][y]==-1)
f[x][y] = time;
for(int j=0;j<4;j++)
{
u = x+dx[j];
v = y+dy[j];
if(u<0 || v<0 || u>498 ||v>498) continue;
if(f[u][v]>time || f[u][v]==-1)
f[u][v] = time;
}
}
// for(int i=0;i<6;i++)
// {
// for(int j=0;j<6;j++)
// cout<<f[i][j]<<" ";
// cout<<endl;
// }
vis[0][0]=true;
node e1,e2;
e1.xx = 0;
e1.yy = 0;
e1.time = 0;
q.push(e1);
while(!q.empty())
{
x=q.front().xx;
y = q.front().yy;
time = q.front().time;
q.pop();
if(f[x][y]==-1) break;
for(int j=0;j<4;j++)
{
u = x+dx[j];
v = y+dy[j];
if(u<0 || v<0 || u>498 ||v>498) continue;
if((f[u][v]==-1||f[u][v]>time+1) && !vis[u][v])
{
vis[u][v] = 1;
e2.xx = u;
e2.yy = v;
e2.time = time+1;
q.push(e2);
}
}
}
if(f[x][y]==-1)
cout<<time;
else
cout<<"-1";
return 0;
}
P1019 [NOIP2000 提高组] 单词接龙
思路
首先进行预处理,
d
i
c
[
i
]
[
j
]
dic[i][j]
dic[i][j]存第i个和第j个串重叠部分的长度。
每个单词最多用两次可以用一个vis数组,初始化为2,dfs时减1。
实现
来自洛谷题解区 MorsLin
#include<bits/stdc++.h>
using namespace std;
int n,dic[21][21],vis[21],ans;
string words[21];
char s;
void f(string a,string b,int x,int y)
{
int a1=a.size()-1,b1=b.size()-1;
for(int i=0;i<=b1;i++) //从第一个开始枚举
{
if(a[0]==b[i]) //如果a的首字母和b中间的字母相同 ,则判断它们能不能接在一起
{
int pos=0,tot=0; //pos是当前a的第几个字母,tot是a和b的重合部分长度
for(int j=i;j<=b1;j++)
{
if(a[pos]==b[j])
{
tot++;
pos++;
if(j==b1&&tot!=min(a1,b1)+1) //如果枚举到了最后,并且a和b没有包含关系,说明可以这么接
dic[x][y]=tot; //记录最小重叠部分的长度
//之所以不break,是因为后面可能还会枚举到更小的接法
//比如 chsese 和 sesettt 显然 chsesesettt 要比chsesettt更优
}
else break;
}
}
}
}
void dfs(int pos,int sum)
{
ans=max(ans,sum); //实时更新ans
for(int i=1;i<=n;i++)
{
if(dic[i][pos]&&vis[i])
{
vis[i]--;
int suml=sum+words[i].size()-dic[i][pos]; //接上新单词"龙"的长度为=旧的长度+新单词长度-重复部分长度
dfs(i,suml); //接上新单词继续搜索
vis[i]++;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>words[i];
vis[i]=2; //初始化vis数组,每个单词能用两次
}
cin>>s;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f(words[i],words[j],i,j); //预处理dic数组
for(int i=1;i<=n;i++)
{
if(words[i][0]==s) //找到开始部分
{
vis[i]--;
dfs(i,words[i].size()); //深搜
vis[i]++;
}
}
cout<<ans;
return 0;
}
欢迎指正-