1.HDU-1518 https://vjudge.net/problem/HDU-1518
Given a set of sticks of various lengths, is it possible to join them end-to-end to form a square?
Input
The first line of input contains N, the number of test cases. Each test case begins with an integer 4 <= M <= 20, the number of sticks. M integers follow; each gives the length of a stick - an integer between 1 and 10,000.
Output
For each case, output a line containing “yes” if is is possible to form a square; otherwise output “no”.
Sample Input
3
4 1 1 1 1
5 10 20 30 40 50
8 1 7 2 6 4 4 3 5
Sample Output
yes
no
yes
经典DFS+剪枝
题意就是好多棍子,看能不能拼成正方形。主要注意的有几点:
1.所有棍子都要用到,不能剩余
2.输入已经保证大于4根棍子了。所以无需判断可能小于3根棍子的情况
3.棍长的总数首先要是4的倍数,才能进行。否则直接输出 “no”
4.当前面前提满足以后,再满足3 根棍子拼好,就完工了。最后一根一定能拼好。
解法就是DFS------->深度优先搜索。DFS的思路就是一个图沿着一条路走下去,当走不下去的时候就回溯到上一结点再去走没有走过的岔路。
换算成数据结构的话,就要有一个“标记”来标记每个结点是否走过。DFS具体的实现方式,常见的一种就是:循环里面嵌套递归,这也算是一个DFS的框架。而剩下的要补充的“题眼”(也就是关键的地方)是要转移的状态。
//主要思路就是DFS搜索边长,不够边长就拼接为边长
#include
#include
#include
#include
using namespace std;
const int N=25;
int n,t,a[N],len;
bool vis[N];
bool cmp(int a,int b) //用于sort从大到小排序
{
return a>b;
}
bool dfs(int count,int start,int goal) //count满足条件的边数,start是遍历起始,goal是拼接所需的下一个边长即目标
{
if(count==3) return true; //因为sum%4=0,故找到3条边一定满足
for(int i=start;i<t;i++)
{
if(!vis[i])
{
vis[i]=true;
if(a[i]==goal) //如果满足了当前目标,count+1,从头开始继续寻找,goal重新变为ll即边长
{
if(dfs(count+1,0,len)) return true;
}
else if(a[i]<goal) //如果比当前所需边长小,那么goal变为能与当前边拼接从而满足要求的边
{
if(dfs(count,i+1,goal-a[i])) return true;
}
vis[i]=false;
}
}
return false;
}
int main()
{
cin>>n;
while(n--)
{
int sum=0;
cin>>t;
for(int i=0;i<t;i++)
{
scanf("%d",&a[i]);
sum=sum+a[i];
}
len=sum/4; //长方形边长
sort(a,a+t,cmp); //从小到大排序,第一个就是最大的了,便于之后操作
memset(vis,0,sizeof(vis)); //每次开始前记得要清除vis
if(t<=3||sum%4!=0) //边数小于3,或者边长综合不是4的倍数,直接no
printf("no\n");
else if(dfs(0,0,len)) printf("yes\n");
else printf("no\n");
}
getchar();getchar();
return 0;
}
2.HDU 1459 非常可乐
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。
Input
三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以"0 0 0"结束。
Output
如果能平分的话请输出最少要倒的次数,否则输出"NO"。
Sample Input
7 4 3
4 1 3
0 0 0
Sample Output
NO
3
#include
#include
#include
#include
using namespace std;
const int N=110;
int s,n,m,count,ans;
bool vis[N][N][N];
bool flag;
void dfs(int x,int y,int z,int count)
{
if(x==s/2&&y==s/2||x==s/2&&z==s/2||y==s/2&&z==s/2)
{
flag=true;
if(count<ans) ans=count;
return ;
}
//接下来分为6种情况
//x->y
if(x>0&&y<n)
{
int t=min(x,n-y);
if(!vis[x-t][y+t][z])
{
vis[x-t][y+t][z]=true;
dfs(x-t,y+t,z,count+1);
vis[x-t][y+t][z]=false;
}
}
//y->x
if(y>0&&x<s)
{
int t=min(y,s-x);
if(!vis[x+t][y-t][z])
{
vis[x+t][y-t][z]=true;
dfs(x+t,y-t,z,count+1);
vis[x+t][y-t][z]=false;
}
}
//x->z
if(x>0&&z<m)
{
int t=min(x,m-z);
if(!vis[x-t][y][z+t])
{
vis[x-t][y][z+t]=true;
dfs(x-t,y,z+t,count+1);
vis[x-t][y][z+t]=false;
}
}
//z->x
if(z>0&&x<s)
{
int t=min(z,s-x);
if(!vis[x+t][y][z-t])
{
vis[x+t][y][z-t]=true;
dfs(x+t,y,z-t,count+1);
vis[x+t][y][z-t]=false;
}
}
//y->z
if(y>0&&z<m)
{
int t=min(y,m-z);
if(!vis[x][y-t][z+t])
{
vis[x][y-t][z+t]=true;
dfs(x,y-t,z+t,count+1);
vis[x][y-t][z+t]=false;
}
}
//z->y
if(z>0&&y<n)
{
int t=min(z,n-y);
if(!vis[x][y+t][z-t])
{
vis[x][y+t][z-t]=true;
dfs(x,y+t,z-t,count+1);
vis[x][y+t][z-t]=false;
}
}
}
int main()
{
while(1)
{
cin>>s>>n>>m;
if(s==0&&n==0&&m==0) break;
if(s%2)
{
cout<<"NO"<<endl; //为奇数则不可能分开
continue;
}
memset(vis,0,sizeof(vis));
vis[s][0][0]=1;
ans=999999999;
flag=false;
dfs(s,0,0,0);
if(flag) cout<<ans<<endl;
else cout<<"NO"<<endl;
}
getchar();getchar();
return 0;
}
3.HDU 1312 red and black
There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can’t move on red tiles, he can move only on black tiles.
Write a program to count the number of black tiles which he can reach by repeating the moves described above.
Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20.
There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.
‘.’ - a black tile
‘#’ - a red tile
‘@’ - a man on a black tile(appears exactly once in a data set)
Output
For each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself).
#include
#include
#include
#include
using namespace std;
const int N=22;
char g[N][N];
bool vis[N][N];
int n,m;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int dfs(int x,int y)
{
int count=1;
vis[x][y]=true;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||a>=n||b<0||b>=m) continue;
if(g[a][b]!='.') continue;
if(vis[a][b]) continue;
count=count+dfs(a,b);
}
return count;
}
int main()
{
while(cin>>m>>n)
{
if(m==0||n==0) break;
int x,y;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
cin>>g[i][j];
if(g[i][j]=='@')
{
x=i;
y=j;
}
}
memset(vis,0,sizeof(vis));
cout<<dfs(x,y)<<endl;
}
getchar();getchar();
return 0;
}
4 .皇后问题 HDU – 2553
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
Input
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
Output
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。
Sample Input
1
8
5
0
Sample Output
1
92
10
#include
#include
#include
#include
using namespace std;
const int N=50;
int n,res,a[N];
bool vis[3][N]; //3个方向,即列,两个斜45度方向上都不能有皇后
void dfs(int row)
{
if(row==n+1)
{
res++;
return ;
}
for(int i=1;i<=n;i++) //对于第row行,枚举每一列
{
if(vis[0][i]==0&&vis[1][row-i+n]==0&&vis[2][row+i]==0)
{
vis[0][i]=vis[1][row-i+n]=vis[2][row+i]=true;
dfs(row+1);
vis[0][i]=vis[1][row-i+n]=vis[2][row+i]=false;
}
}
}
int main()
{
for(n = 1; n <= 10; n++) //先打表,把1到10的先算出来,按要求输出即可,不然会超时的
{
memset(vis,0,sizeof(vis));
res = 0;
dfs(1);
a[n] = res;
}
while(scanf("%d",&n), n)
{
printf("%d\n",a[n]);
}
getchar();getchar();
return 0;
}
5.HDU 1175 连连看
Problem Description
“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。
Input
输入数据有多组。每组数据的第一行有两个正整数n,m(0
Output
每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。
Sample Input
3 4
1 2 3 4
0 0 0 0
4 3 2 1
4
1 1 3 4
1 1 2 4
1 1 3 3
2 1 2 4
3 4
0 1 4 3
0 2 4 1
0 0 0 0
2
1 1 2 4
1 3 2 3
0 0
Sample Output
YES
NO
NO
NO
NO
YES
#include
#include
#include
#include
using namespace std;
const int N=1010;
int g[N][N];
bool vis[N][N];
int n,m,x1,x2,y1,y2,q;
bool flag;
int dx[4]={0,-1,0,1},dy[4]={1,0,-1,0};
void dfs(int x,int y,int dic,int turn) //dic表示当前方向,turn表示转弯次数
{
if(turn>2||flag) return; //转弯次数大于就终止
if(turn==2&&(x-x2)!=0&&(y-y2)!=0) return;//剪枝:判断两次转弯后是否与目标在同一直线上
if(x==x2&&y==y2&&turn<=2)
{
flag=true;
return ;
}
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<1||a>n||b<1||b>m) continue;
if(vis[a][b]) continue;
if(g[a][b]==0||(a==x2&&b==y2))
{
vis[a][b]=true;
if(dic==-1||dic==i) //如果为-1即在起点,或者与当前方向一致
dfs(a,b,i,turn);
else //与当前方向不一致
dfs(a,b,i,turn+1);
vis[a][b]=false;
}
}
return ;
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0) break;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&g[i][j]);
cin>>q;
for(int i=1;i<=q;i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
memset(vis,0,sizeof(vis));
flag=false;
if(g[x1][y1]==g[x2][y2]&&g[x1][y1])
dfs(x1,y1,-1,0);
if(flag) printf("YES\n");
else printf("NO\n");
}
}
getchar();getchar();
return 0;
}