在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政 区划十分特殊,刚好构成一个N行M列的矩形,如上图所示,其中每个格子都代表一座城 市,每座城市都有一个海拔高度。 为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施 有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的 蓄水池中。因此,只有与湖泊毗邻的第1行的城市可以建造蓄水厂。而输水站的功能则是通 过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是 存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。 由于第N行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利 设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干 旱区中不可能建有水利设施的城市数目。
输入的每行中两个数之间用一个空格隔开。 输入的第一行是两个正整数N和M,表示矩形的规模。 接下来N行,每行M个正整数,依次代表每座城市的海拔高度。
输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少 建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有 几座干旱区中的城市不可能建有水利设施。
2 5
9 1 5 4 3
8 7 6 1 2
1
1
【数据范围】 本题共有10个测试数据,每个数据的范围如下表所示: 测试数据编号 能否满足要求 N M 1 不能 ≤ 10 ≤ 10 2 不能 ≤ 100 ≤ 100 3 不能 ≤ 500 ≤ 500 4 能 = 1 ≤ 10 5 能 ≤ 10 ≤ 10 6 能 ≤ 100 ≤ 20 7 能 ≤ 100 ≤ 50 8 能 ≤ 100 ≤ 100 9 能 ≤ 200 ≤ 200 10 能 ≤ 500 ≤ 500 对于所有的10个数据,每座城市的海拔高度都不超过10^6
样例2 说明
数据范围
这个题简单点说,就是给一个每个坐标有高度的棋盘,然后要在棋盘的一边,在一些点上灌水,根据水往低处流的原理,让水能流到对面一边,要求判断是否能让另一边全部有水,如果能,求出最少要给多少个点浇水,才能使另一边全部有水。
对于第一问来说,可以从出发的一边开始,将这一行所有的格子都浇上水,然后判断对面那一边是否所有的格子都有水。第二问处理复杂很多,需要从出发的一边,每次只对一个格子浇水,然后找到对面那一边的有水的格子的区间(可以证明这个区间是连续的,我之前的一篇文章里有证明),将每个格子对应的区间当成线段来看待,第二问就转化成了给定m条线段,要覆盖[1,m]区间至少要多少条的问题,可以用贪心来做。
如图,先将所有线段根据左端点升序排序,然后每次寻找一条线段,保证这条线段与之前的覆盖区间重合或相连,且线段的右端点大于之前覆盖区间的右端点(就是说加入这条线段能增加覆盖区间),在满足这些条件的线段中找右端点最大的一条,并插入覆盖区间,更新覆盖区间。可以证明,这个贪心思想是正确的。
#include
#include
#include
#include
#include
#define mem(array,num) memset(array,num,sizeof(array))
#define MAXN 600
#define lowbit(num) ((x&(-x))
using namespace std;
struct Line
{
int num,L,R; //左端点、右端点
}line[MAXN];
int map[MAXN][MAXN],xx[]={1,-1,0,0},yy[]={0,0,1,-1};
int visit[MAXN][MAXN];
int high[MAXN][MAXN];
int n,m;
bool cmp(Line a,Line b)
{
if(a.L!=b.L) return a.L=1&&newx<=n&&newy>=1&&newy<=m)
if(high[newx][newy]maxRight&&line[i].R>maxR)
{
maxR=line[i].R;
nextLine=i; //下一条加入的线段标记为i
}
}
}
ans++;
pointer=nextLine;
maxRight=maxR;
}
printf("1\n%d\n",ans);
}
int main()
{
int noWaterCityNum=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&high[i][j]);
for(int i=1;i<=m;i++)
flood(1,i);
for(int i=1;i<=m;i++)
if(!map[n][i])
noWaterCityNum++;
if(noWaterCityNum)
{
printf("0\n%d\n",noWaterCityNum);
system("pause");
return 0;
}
for(int i=1;i<=m;i++) //以棋盘上的点(1,i)作为起点开始染色
{
bool flag=false;
mem(map,0); //棋盘清空
mem(visit,0);
flood(1,i); //灌水法染色
for(int j=1;j<=m+1;j++) //从1到m寻找最后一行(n,j),找到(1,i)染色后在最后一行留下的区间
{
if(map[n][j]&&!flag)
{
if(!line[i].L) //找到了区间起点
{
line[i].L=j;
flag=true;
}
}
else if(!map[n][j]&&flag)
{
line[i].R=j-1; //找到了区间终点
break;
}
}
}
greed(); //贪心求最少要几个输水站
system("pause");
return 0;
}
给定N(小于等于8)个点的地图,以及地图上各点的相邻关系,请输出用4种颜色将地图涂色的所有方案数(要求相邻两点不能涂成相同的颜色)
数据中0代表不相邻,1代表相邻
第一行一个整数n,代表地图上有n个点
接下来n行,每行n个整数,每个整数是0或者1。第i行第j列的值代表了第i个点和第j个点之间是相邻的还是不相邻,相邻就是1,不相邻就是0.
我们保证a[i][j] = a[j][i] (a[i,j] = a[j,i])
染色的方案数
8
0 0 0 1 0 0 1 0
0 0 0 0 0 1 0 1
0 0 0 0 0 0 1 0
1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
1 0 1 0 0 0 0 0
0 1 0 0 0 0 0 0
15552
n<=8
题目太简单了就不多说了,数据太弱,很简单的DFS,用数组保存当前各个点的颜色就行了,没什么要注意的地方
#include
#include
#include
#define MAXN 10
int color[MAXN],relative[MAXN][MAXN]; //color[i]=i点的颜色,relative[i][j]=1表示i和j相邻
int n,totalSolution=0; //totalSolution=总计方案数
void dfs(int x) //对点x染色
{
if(x>n) //所有点染色完了
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j)
if(relative[i][j]) //寻找两个相邻的点i,j
if(color[i]==color[j])
return; //有相邻的点颜色相同,直接返回
totalSolution++; //符合条件,增加方案数,返回
return;
}
for(int i=1;i<=4;i++) //对点x染颜色i
{
color[x]=i;
dfs(x+1);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&relative[i][j]);
dfs(1);
printf("%d\n",totalSolution);
return 0;
}
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:
43#9865#045
+ 8468#6633
44445506978
其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。
现在,我们对问题做两个限制:
首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。
其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:但是这N个字母并不一定顺序地代表0到N-1)。输入数据保证N个字母分别至少出现一次。
BADC
+ CBDA
DCCC
上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解,
输入包含4行。第一行有一个正整数N(N<=26),后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。
输出包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。
5
ABCED
BDACE
EBBAA
1 0 3 4 2
对于30%的数据,保证有N<=10;
对于50%的数据,保证有N<=15;
对于全部的数据,保证有N<=26。
看得出来,虫食算是个线性方程组,而且官方标程也是用高斯消元来解的,不过比赛时很多同学不会高斯消元,都是用DFS爆搜+强剪枝骗分的,DFS的思路很清晰,首先要离散化,如果不想用map来存答案的话,就需要把输入数据中的字母统计成一个字母表,给每个字母一个标号当成下标,然后根据这个字母表中字母的顺序,依次枚举每个字母对应的数字,所有字母枚举完后,检查答案是否正确,如果正确,输出答案,结束程序。
不过这样做肯定没多少分,因为N<=26,范围很大,所以需要剪枝,具体剪枝思路如下:
1、如果当前位i,a[i]、b[i]、c[i]已知,就检查a[i]+b[i]算上进位和不算进位是否等于c[i],都不等于,则不再继续搜索
2、如果当前位i,a[i]、b[i]、c[i]中有两个已知,就根据算上进位和不算上进位的情况考虑另一个位置的数字的答案,若这个答案对应的数字之前已经被使用过了,则不再继续搜索。
这两条剪枝非常重要,有好几个判断,少一个判断都不行。
另外程序还有几个坑点,我来总结下:
1、字母表应该由低位的字母到高位的字母依次插入,因为搜索时如果低位字母已知的多一些,剪枝效果会好很多,这样的顺序可以保证低位的数字先猜出来,高位的后猜出来。如果插入顺序反了,会导致搜索效率严重下降,剪枝的优势体现不出来。
2、如果找到了答案,立刻停止搜索,因为搜索树中每一个结点的儿子很多,如果不停止搜索,还会继续搜索那些没有用的子树,这些部分非常大,会严重影响搜索速度。
总结:如果会高斯消元的话,我不建议考试时用爆搜做这个题,因为爆搜加剪枝不仅写起来麻烦,而且很容易因为剪枝写错了写漏了而TLE扣分,甚至会接近爆零,高斯消元也不难,代码简单,不妨考虑学学高斯消元,以备不时之需,小心考试时的高斯消元题不会做,用爆搜去做很难拿高分。
下面是代码
#include
#include
#include
#include
#include
#define MAXN 100
using namespace std;
string A,B,C,word; //A+B=C,最高位为n-1,最低位为0!
int n,num[MAXN],ans[MAXN]; //N进制数,ans[]数组保存答案
int hash[270],used[27]; //hash[i]=1表示字母i已经加入了单词表,permutation[i]=字母i在word表里的位置,used[i]=1表示已经尝试过了字母i的对应数字
bool hasFinished=false;
bool isWrongAns() //检查已经算出来的答案是否正确,如果答案没算完,但是算出来的部分正确,就当成正确
{
int c,g=0;
for(int i=n-1;i>=0;i--) //从低位到高位
{
if(A[i]>=n||B[i]>=n||C[i]>=n) //有字母没有被替换
return false;
c=A[i]+B[i]+g;
if(c%n!=C[i]) //a+b!=c 则说明猜的数字有问题
return true;
g=c/n; //计算进位
}
return false;
}
bool check() //检查当前状态下已经算出的部分是否有条件冲突
{
int c,g=0,x1,x2;
//1、已知a[i] b[i] c[i]
for(int i=n-1;i>=0;i--)
{
if(A[i]>=n||B[i]>=n||C[i]>=n)
continue;
c=(A[i]+B[i])%n; //c=a[i]+b[i]
if(!(c==C[i]||(c+1)%n==C[i])) return true; //无论进不进位,答案都不对
}
//2、已知a[i] b[i]
for(int i=n-1;i>=0;i--)
{
if(!(A[i]=n))
continue;
x1=(A[i]+B[i])%n; //c=a[i]+b[i]
x2=(A[i]+B[i]+1)%n;
if(used[x1]&&used[x2]) return true; //无论进不进位,答案都不对
}
//3、已知a[i] c[i]
for(int i=n-1;i>=0;i--)
{
if(!(A[i]=n&&C[i]=0;i--)
{
if(!(A[i]>=n&&B[i]=0;i--) //用数字i去尝试代替第p个字母
{
if(!used[i]) //数字i没有用过
{
used[i]=true;
copyA=A,copyB=B,copyC=C; //将A、B、C拷贝保存
A=change(copyA,word[p],i);
B=change(copyB,word[p],i);
C=change(copyC,word[p],i);
num[p]=i; //第p个字母对应的数字是i
dfs(p+1); //尝试下一个字母对应的数字
A=copyA,B=copyB,C=copyC;
used[i]=false;
}
}
}
int main()
{
scanf("%d",&n);
cin>>A>>B>>C;
for(int i=n-1;i>=0;i--)
{
if(!hash[A[i]])
{
word+=A[i]; //如果之前没有加入过这个字母,将这个字母加入单词表
hash[A[i]]=1;
}
if(!hash[B[i]])
{
word+=B[i]; //如果之前没有加入过这个字母,将这个字母加入单词表
hash[B[i]]=1;
}
if(!hash[C[i]])
{
word+=C[i]; //如果之前没有加入过这个字母,将这个字母加入单词表
hash[C[i]]=1;
}
}
dfs(0);
return 0;
}
Description
Input
Output
Sample Input
5 6 7 1 2 2 3 2 4 3 3 3 4 2 4 1 3 4 1 4 6 2 1 3 5 2 0 5 4 3 2
Sample Output
11
Source
这个题其实是个图论的题,不适合用DFS做,不过郭老师的搜索课件里有提过这个题,我就用DFS来做试试。
用三元组(n,cost,dist)来表示一个搜索状态,搜索到了点n,总的花费是cost,距离是dist
DFS本来跑得就慢,你们懂的,所以需要很多剪枝优化:
1、如果之前已经求过到达点n、花费为cost的最小距离,且之前的解更优,就不继续搜索
2、如果花费cost>K,就是说没这么多钱去交过路费,也不继续搜索
3、每次dfs时若点n被访问过,不继续搜索,否则标记点n被访问过
缺一条剪枝都不行,这个题时间要求太苛刻了
#include
#include
#include
#define MAXE 110
#define MAXV 10100
#define MAXM 10100
#define INF 0x3f3f3f3f
int f[MAXE][MAXM]; //f[i][cost]=到达i点,花了过路费cost时的最少距离
struct edge
{
int u,v,w,cost; //起点,终点,距离,过路费
int next;
}edges[MAXV];
int head[MAXE],nCount=0,K,N,R,totalCost=0,totalDist=0,minDist=INF;
bool visit[MAXE];
void AddEdge(int U,int V,int W,int COST)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].w=W;
edges[nCount].cost=COST;
edges[nCount].next=head[U];
head[U]=nCount;
}
void dfs(int n) //到达了n点,一共花了过路费cost元,最短距离为dist
{
if(n==N)
{
if(totalDistK) continue;
if(totalDist+edges[p].w>minDist||totalDist+edges[p].w>f[edges[p].v][totalCost+edges[p].cost])
continue;
totalCost+=edges[p].cost;
totalDist+=edges[p].w;
f[edges[p].v][totalCost]=totalDist;
visit[edges[p].v]=true;
dfs(edges[p].v);
visit[edges[p].v]=false;
totalCost-=edges[p].cost;
totalDist-=edges[p].w;
}
}
int main()
{
int minAns=INF;
memset(head,-1,sizeof(head));
memset(f,INF,sizeof(f));
scanf("%d%d%d",&K,&N,&R);
for(int i=1;i<=R;i++)
{
int u,v,w,cost;
scanf("%d%d%d%d",&u,&v,&w,&cost);
AddEdge(u,v,w,cost);
}
dfs(1);
if(minDist
小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他
们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向Z 博士请教,
Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。
靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有9 个3 格宽×3 格
高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入1 到9 的数字。每个数字在每个小九宫格内不能
重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即
每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。
上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红
色区域)每个格子为9 分,再外面一圈(蓝色区域)每个格子为8 分,蓝色区域外面一圈(棕
色区域)每个格子为7 分,最外面一圈(白色区域)每个格子为6 分,如上图所示。比赛的
要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法),而且要争取
更高的总分数。而这个总分数即每个方格上的分值和完成这个数独时填在相应格上的数字
的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为2829。游
戏规定,将以总分数的高低决出胜负。由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能
够得到的最高分数。
一共 9 行。每行9 个整数(每个数都在0—9 的范围内),表示一个尚未填满的数独方
格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。
输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。
【输入输出样例 1】
7 0 0 9 0 0 0 0 1
1 0 0 0 0 5 9 0 0
0 0 0 2 0 0 0 8 0
0 0 5 0 2 0 0 0 3
0 0 0 0 0 0 6 4 8
4 1 3 0 0 0 0 0 0
0 0 7 0 0 2 0 9 0
2 0 1 0 6 0 8 0 4
0 8 0 5 0 4 0 1 2【输入输出样例 2】
0 0 0 7 0 2 4 5 3
9 0 0 0 0 8 0 0 0
7 4 0 0 0 5 0 1 0
1 9 5 0 8 0 0 0 0
0 7 0 0 0 0 0 2 5
0 3 0 5 7 9 1 0 8
0 0 0 6 0 1 0 0 0
0 6 0 9 0 0 0 0 1
0 0 0 0 0 0 0 0 6
【输入输出样例 1】
2829
【输入输出样例 1】
2852
【数据范围】
40%的数据,数独中非0 数的个数不少于30。
80%的数据,数独中非0 数的个数不少于26。
100%的数据,数独中非0 数的个数不少于24。
Wikioi上把这个题标了个启发式搜索的标签,于是有人就说这个题要用A*做,我只能呵呵了,这个题和A*没半毛钱关系,因为题目是填数独,作为NP完全问题,数独只能用搜索做,而且是DFS(BFS只能求最少步数之类的题,和这个题没关系)
这个题需要对搜索顺序进行排序,以我们人类思维来做数独的话,我们一般会先填那些可选择的数字少的空,再填可选择的数字多的空,因为先填可选择的数字少的空,就能对后面可选择数字多的空产生限制条件,这个不难理解。从搜索树的角度来讲,这样做,可以让靠近搜索树根的结点儿子少,远离树根的结点儿子多,加上剪枝,剪同样一个枝能少搜索很多点,效果很好,这样能够提高搜索速度。有人说这就是启发式搜索了,我认为调整搜索顺序只能算作剪枝
思路:首先记录下每行每列有数字的格子个数,然后根据有数字的格子个数,对行、列进行降序排序,再按照这个排序顺序,不断寻找有空格的格子,将它加入搜索顺序表中,DFS过程就是对于搜索顺序表中的每个格子,依次尝试不同的数字,同时需要加上剪枝:只尝试同一行、同一列、同一小方格没有出现过的数字,这样做也能大大优化搜索效率。
当然还有另一种做法DLX(Dancing Links),这种做法就是Wikioi所说的启发式搜索了,速度很快。
#include
#include
#include
#include
#define MAXN 100
#define MAXNUM 10
using namespace std;
int belong[MAXN][MAXN]={ //belong[i][j]=(i,j)所属的方格
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}
};
int score[MAXN][MAXN]={ //score[i][j]=(i,j)格子对应的权值
{0,0,0,0,0,0,0,0,0,0},
{0,6,6,6,6,6,6,6,6,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,9,10,9,8,7,6},
{0,6,7,8,9,9,9,8,7,6},
{0,6,7,8,8,8,8,8,7,6},
{0,6,7,7,7,7,7,7,7,6},
{0,6,6,6,6,6,6,6,6,6},
{0,0,0,0,0,0,0,0,0,0},
};
int map[MAXN][MAXN]; //map[][]保存数独棋盘
int xVisit[MAXN][MAXNUM]; //xVisit[i][j]=1表示行i上数字j已经用过
int yVisit[MAXN][MAXNUM]; //yVisit[i][j]=1表示列i上数字j已经用过
int squareVisit[MAXN][MAXNUM]; //squareVisit[i][j]=1表示在小九宫格i中数字J用过
int xPermutation[MAXN]; //行的搜索顺序
int yPermutation[MAXN]; //列的搜索顺序
int sortX[MAXN],sortY[MAXN];
int hasNumOnX[MAXN]; //每一行有数字的格子个数
int hasNumOnY[MAXN]; //每一列有数字的格子个数
int blankNum=0; //空白格子个数
int maxScore=-1; //最大分数
bool XCmp(int i,int j) //对行排序的比较函数
{
return hasNumOnX[sortX[i]]>hasNumOnX[sortX[j]];
}
bool YCmp(int i,int j) //对列排序的比较函数
{
return hasNumOnY[sortY[i]]>hasNumOnY[sortY[j]];
}
void getPermutation() //对搜索顺序,根据行/列上有数字的个数降序排序,先搜索有数字多的
{
sort(sortX+1,sortX+9+1,XCmp);
sort(sortY+1,sortY+9+1,YCmp);
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
if(!map[sortX[i]][sortY[j]]) //空白格子
{
xPermutation[blankNum]=sortX[i]; //在搜索顺序中记录下它
yPermutation[blankNum]=sortY[j];
blankNum++;
}
}
void refreshScore() //更新最大分数
{
int sum=0;
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
sum+=score[i][j]*map[i][j];
if(maxScore
题目描述 Description
任何一个正整数都可以用2的幂次方表示.
例如:137=2^7+2^3+2^0
同时约定次方用括号来表示,即a^b可表示为a(b)
由此可知,137可表示为:2(7)+2(3)+2(0)
进一步:7=2^2+2+2^0 (2^1用2表示)
3=2+2^0
所以最后137可表示为:2(2(2)+2+2(0))+2(2+2(0))+2(0)
又如:1315=2^10+2^8+2^5+2+1
所以1315最后可表示为:2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
正整数n
符合约定的n的0,2表示(在表示中不能有空格)
【输入样例1】
137
【输入样例2】
1315
【输出样例1】
2(2(2)+2+2(0))+2(2+2(0))+2(0)
【输出样例2】
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
n为2的指数<=1100586419200
很恶心的搜索题,裸DFS加上变态的模拟,刷尿我了
#include
#include
#include
#define LEN 64
void dfs(long long int n) //将n拆成2的幂次方
{
bool flag=false; //若flag=true,则表明是括号后第一个数字
for(long long int i=0;i1,继续搜索
printf(")");
}
}
n=n<<1;
}
}
int main()
{
long long int n;
scanf("%lld",&n);
dfs(n);
printf("\n");
return 0;
}
7、POJ 2488 A Knight's Journey
Description
Input
Output
Sample Input
3 1 1 2 3 4 3
Sample Output
Scenario #1: A1 Scenario #2: impossible Scenario #3: A1B3C1A2B4C2A3B1C3A4B2C4
Source
今天整个人状态像坨翔一样。。。这个题就是个简单的DFS,只不过要求输出的遍历方案为字典序最小的,这不难实现,只需要在搜索时按照下图这样的顺序依次走就行,看了这图就能明白的
另外一定要注意:如果所有格子当成起点都尝试一遍行不通以后,一定要输出impossible,否则会WA!
#include
#include
#include
#include
#include
using namespace std;
int xx[]={-1,1,-2,2,-2,2,-1,1},yy[]={-2,-2,-1,-1,1,1,2,2};
bool hasVisit[30][30];
int n,m,depth=0; //棋盘大小n*m
string move; //马的遍历顺序
bool isAllVisited() //检查是否所有点都被遍历过
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!hasVisit[i][j])
return false;
return true;
}
bool inMap(int x,int y)
{
if(x<1||x>n||y<1||y>m) return false;
return true;
}
bool dfs(int x,int y,int step,int Case) //马的位置是(x,y)
{
hasVisit[x][y]=true;
move+=y-1+'A';
move+=x-1+'1';
if(step==depth) //所有点都被访问过了
{
cout<<"Scenario #"<26||n>8||m>8||n<=2||m<=2)
{
printf("Scenario #%d:\nimpossible\n\n",Case);
continue;
}
depth=n*m;
bool flag=true;
for(int i=1;i<=n&&flag;i++)
for(int j=1;j<=m;j++)
{
if(dfs(i,j,1,Case))
{
flag=false;
break;
}
}
if(flag)
printf("Scenario #%d:\nimpossible\n\n",Case);
}
return 0;
}
在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局。
● ○ ● ○ ● ○ ● ● ○ ● ○ ○ ● ○
从文件中读入一个4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地带用O表示。
用最少的步数移动到目标棋局的步数。
BWBO
WBWB
BWBW
WBWO
5
这是一道很经典的广搜题,需要运用判重的知识,广搜过程应该很好理解,就是每次取出队首的状态,在队首的状态棋盘中找到两个空格,并将空格和相邻的棋子交换,要注意这里有先手和后手之分,BFS的状态应该包含棋盘、搜索步数、哈希值和最近下的棋的颜色,最近下的是白色,那么空格只能和黑棋交换,否则空格只能和白棋交换。判重也是一样,对于状态(s,最后下的是黑棋)和(s,最后下的是白棋)两种状态来说,虽然棋盘数组是一样的,但是最后下的棋颜色不同,最终的结果也会不同,因此判重数组应该是两维的:第一维是棋盘的哈希值,第二维是棋盘的最后下的棋的颜色,另外要注意,如果用三进制表示棋盘的哈希值,棋盘的哈希值<=3^16,这个范围明显超出了int表达范围,因此需要用Map容器保存棋盘哈希值这一个维度,也可以用string类型保存这个哈希值,或许会简单很多,但是要牺牲一点空间,如果想要空间不要时间,也可以用康托展开去保存哈希值,写起来复杂很多。
#include
#include
#include
#include
#include
题目描述 Description
年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他没想到的是那辆车属于警察局,并且车上装有用于发射车子移动路线的装置。
那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息。
编写程序,通过使用一张小镇的地图帮助警察局找到那辆车。程序必须能表示出该车最终所有可能的位置。
小镇的地图是矩形的,上面的符号用来标明哪儿可以行车哪儿不行。“.”表示小镇上那块地方是可以行车的,而符号“X”表示此处不能行车。拉尔夫所开小车的初始位置用字符的“*”表示,且汽车能从初始位置通过。
汽车能向四个方向移动:向北(向上),向南(向下),向西(向左),向东(向右)。
拉尔夫所开小车的行动路线是通过一组给定的方向来描述的。在每个给定的方向,拉尔夫驾驶小车通过小镇上一个或更多的可行车地点。
输入文件的第一行包含两个用空格隔开的自然数R和C,1≤R≤50,1≤C≤50,分别表示小镇地图中的行数和列数。
以下的R行中每行都包含一组C个符号(“.”或“X”或“*”)用来描述地图上相应的部位。
接下来的第R+2行包含一个自然数N,1≤N≤1000,表示一组方向的长度。
接下来的N行幅行包含下述单词中的任一个:NORTH(北)、SOUTH(南)、WEST(西)和EAST(东),表示汽车移动的方向,任何两个连续的方向都不相同。
输出文件应包含用R行表示的小镇的地图(象输入文件中一样),字符“*”应该仅用来表示汽车最终可能出现的位置。
4 5
.....
.X...
...*X
X.X..
3
NORTH
WEST
SOUTH
.....
*X*..
*.*.X
X.X..
这个题也是广搜题,因为汽车正在行驶时的方向对最终结果有影响,所以BFS的状态应该是一个三元组(x,y,n),表示汽车当前坐标位于(x,y),正在以第n种方向行驶,判重数组也是三维的,BFS过程中状态转换时,就沿着第n种方向不断前进直到有障碍物,其间每经过一个点(newx,newy),就将状态(newx,newy,n+1)入队,BFS循环每次开始时,将队首出队,若队首正在行驶的方向大于n,则说明所有的方向都行驶完了,在地图上记录下汽车的最终位置后跳过,记住是跳过!因为汽车的终止位置不止一个
#include
#include
#include
#define MAXN 60
#define MAXM 1100
#define MAXQ 1000000
struct node
{
int NumOfDir; //当前正在用的方向
int x,y; //当前车子坐标
}first,q[MAXQ];
int h=0,t=1;
int map[MAXN][MAXN],R,C,sx,sy,n; //map[i][j]=1表示障碍物,2表示车最终可能出现的位置,初始坐标(sx,sy)
int direct[MAXM]; //
int xx[]={-1,1,0,0},yy[]={0,0,-1,1};
bool inQueue[MAXN][MAXN][MAXM]; //判重用数组,inQueue[i][j][n]=true表示车子在(i,j),正在用第n个方向的状态在队列里
bool inMap(int x,int y) //判定(x,y)是否在棋盘内
{
if(x<1||x>R||y<1||y>C) return false;
return true;
}
void bfs()
{
inQueue[first.x][first.y][first.NumOfDir]=true;
q[h]=first; //初始状态入队
while(hn) //所有的方向都用完了
{
map[now.x][now.y]=2;
continue;
}
node next=now;
next.NumOfDir++;
while(1)
{
next.x+=xx[direct[now.NumOfDir]];
next.y+=yy[direct[now.NumOfDir]];
if(map[next.x][next.y]||!inMap(next.x,next.y)) break;
if(!inQueue[next.x][next.y][next.NumOfDir])
{
q[t++]=next;
inQueue[next.x][next.y][next.NumOfDir]=true;
}
}
}
}
int main()
{
char s[MAXN];
scanf("%d%d",&R,&C);
for(int i=1;i<=R;i++)
{
scanf("%s",s+1);
for(int j=1;j<=C;j++)
{
if(s[j]=='X') map[i][j]=1;
else if(s[j]=='*')
{
sx=i;
sy=j;
}
}
}
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
if(s[0]=='N') direct[i]=0;
if(s[0]=='S') direct[i]=1;
if(s[0]=='W') direct[i]=2;
if(s[0]=='E') direct[i]=3;
}
first.x=sx,first.y=sy;
first.NumOfDir=1;
bfs();
for(int i=1;i<=R;i++)
{
for(int j=1;j<=C;j++)
{
if(map[i][j]==0) printf(".");
if(map[i][j]==1) printf("X");
if(map[i][j]==2) printf("*");
}
printf("\n");
}
return 0;
}
Description
Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a pointN (0 ≤N ≤ 100,000) on a number line and the cow is at a pointK (0 ≤K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.
* Walking: FJ can move from any point X to the points X - 1 orX+ 1 in a single minute
* Teleporting: FJ can move from any point X to the point 2 × X in a single minute.
If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?
Input
Output
Sample Input
5 17
Sample Output
4
Hint
Source
USACO的题太SB了,所以这题就不细说了,注意检查是否越界,其他没什么了,裸BFS。
#include
#include
#include
#include
#define MAXN 101000
using namespace std;
bool inQueue[MAXN];
int n,k;
struct node
{
int pos,step;
}first;
queueq;
bool inMap(int x)
{
if(x<0||x>100000) return false;
return true;
}
void bfs()
{
q.push(first);
inQueue[first.pos]=true;
while(!q.empty())
{
node now=q.front();
q.pop();
if(now.pos==k)
{
printf("%d\n",now.step);
return;
}
if(inMap(now.pos*2))
if(!inQueue[now.pos*2])
{
node next=now;
next.pos*=2;
next.step+=1;
inQueue[next.pos]=true;
q.push(next);
}
if(inMap(now.pos+1))
if(!inQueue[now.pos+1])
{
node next=now;
next.pos+=1;
next.step+=1;
inQueue[next.pos]=true;
q.push(next);
}
if(inMap(now.pos-1))
if(!inQueue[now.pos-1])
{
node next=now;
next.pos-=1;
next.step+=1;
inQueue[next.pos]=true;
q.push(next);
}
}
}
int main()
{
scanf("%d%d",&n,&k);
first.pos=n;
first.step=0;
bfs();
return 0;
}
4、Wikioi 1225 八数码难题
Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.
问题描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入初试状态,一行九个数字,空格用0表示
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
283104765
4
按理说这个题应该算作A*或者IDA*的题,不过Wikioi的数据太弱了,BFS暴力也能过,只能呵呵了,POJ爱卡常数,同样的代码在POJ没办法过
#include
#include
#include
#include
有一个5×5的棋盘,上面有一些格子被染成了黑色,其他的格子都是白色,你的任务的对棋盘一些格子进行染色,使得所有的黑色格子能连成一块,并且你染色的格子数目要最少。读入一个初始棋盘的状态,输出最少需要对多少个格子进行染色,才能使得所有的黑色格子都连成一块。(注:连接是指上下左右四个方向,如果两个黑色格子只共有一个点,那么不算连接)
输入包括一个5×5的01矩阵,中间无空格,1表示格子已经被染成黑色。
输出最少需要对多少个格子进行染色
11100
11000
10000
01111
11111
1
这个题由于数据范围不大(棋盘5*5),因此深度最大不超过25(搜索深度等于要染色的格子数),而这个题又是要求最少步数,因此可以使用迭代加深搜索,迭代加深搜索的细节不再赘述,下面看下这个题需要注意的一些细节:
1、每次搜索从棋盘左上角(1,1)开始,搜索状态为三元组(x,y,step),表示当前已经搜索到点(x,y),在深度step,搜索过程中寻找要么与step同一行,在step后面的白色格子染色,要么寻找比step行数大的格子,考虑下图的棋盘状况:
当当前搜索格子位于点A(如下图时),这一步的搜索可以转移到绿色格子的状态
而整个搜索从左上角的点(1,1),深度为0开始,因此整个搜索顺序如下图
下图中黄色的格子将以箭头所示的顺序尝试下一步的DFS深搜操作
代码:
#include
#include
#include
#define MAXN 100
#define next(x,y,step) (y==5?dfs(x+1,y,step):dfs(x,y+1,step))
using namespace std;
int t[MAXN][MAXN],map[MAXN][MAXN],depth;
bool ans=0;
int xx[]={1,-1,0,0},yy[]={0,0,1,-1};
void del(int x,int y) //递归将与(x,y)相邻的格子都刷白
{
int newx,newy;
t[x][y]=0;
for(int dir=0;dir<4;dir++)
{
newx=x+xx[dir];
newy=y+yy[dir];
if(newx<1||newx>5||newy<1||newy>5||!t[newx][newy])
continue;
del(newx,newy);
}
}
bool check() //检查所有的黑色格子是否连在一起
{
for(int i=1;i<=5;i++)
for(int j=1;j<=5;j++)
t[i][j]=map[i][j]; //将Map数组拷贝到t数组中,之后对t数组进行操作
bool flag=false;
for(int i=1;i<=5;i++)
for(int j=1;j<=5;j++)
if(t[i][j]) //找到了一个黑色格子
{
if(!flag) //第一块黑色区域
{
del(i,j); //将这一块都刷白
flag=true;
}
else return false; //找到新的黑色区域,则表明黑色区域有多块
}
return true;
}
void dfs(int x,int y,int step) //由(x,y)开始搜索,找一个白格染成黑色,搜索步数为step
{
if(step==depth) //到达搜索深度
{
if(check()) //如果达到了目标状态,则找到了答案
ans=1;
return;
}
if(ans||x==6) return;
for(int i=y;i<=5;i++) //将本行的格子染色
{
if(map[x][i]) continue; //如果是黑格子就跳过
map[x][i]=1; //将这个白格子染黑
next(x,i,step+1);
map[x][i]=0; //回溯后恢复
}
for(int i=x+1;i<=5;i++)
{
for(int j=1;j<=5;j++)
{
if(map[i][j]) continue; //如果是黑格子就跳过
map[i][j]=1; //将这个白格子染黑
next(i,j,step+1);
map[i][j]=0; //回溯后恢复
}
}
return;
}
int main()
{
char s[10];
for(int i=1;i<=5;i++)
{
scanf("%s",s+1);
for(int j=1;j<=5;j++)
map[i][j]=s[j]-'0';
}
for(depth=0;depth<=25;depth++)
{
dfs(1,1,0);
if(ans)
{
printf("%d\n",depth);
system("pause");
return 0;
}
}
system("pause");
return 0;
}
Description
Input
Output
Sample Input
2 0 0 0 0 3 0 1 2 1 1 2 2 2 1 0
Sample Output
0 3
Source
第二张图是IDA*时,以与连通块相邻的点为起点进行flood操作的搜索方向
#include
#include
#include
#define MAXN 10
int map[MAXN][MAXN],status[MAXN][MAXN];
//map数组保存棋盘,status[i][j]=1表示(i,j)在联通块内,2表示(i,j)不在联通块但和联通块相邻,0表示(i,j)不和联通块相邻
int N,xx[]={1,-1,0,0},yy[]={0,0,1,-1},ans,depth;
bool inMap(int x,int y) //判定(x,y)是否越界
{
if(x<1||x>N||y<1||y>N)
return false;
return true;
}
void flood(int x,int y,int color) //以(x,y)为起点、color色的格子进行flood操作
{
status[x][y]=1;
for(int dir=0;dir<4;dir++)
{
int newx=x+xx[dir],newy=y+yy[dir];
if(!inMap(newx,newy)) continue;
if(status[newx][newy]==1) continue; //在联通块内,跳过
if(map[newx][newy]==color) //相邻格子(newx,newy)是color色且和联通块相邻
flood(newx,newy,color);
else //相邻格子(newx,newy)不和联通块相邻,标记它为和联通块相邻
status[newx][newy]=2;
}
}
int h() //估价函数,h()=当前状态下棋盘中的颜色种类数,也就是至少要染h()次
{
int sum=0;
bool flag[6];
memset(flag,false,sizeof(flag));
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(!flag[map[i][j]]&&status[i][j]!=1)
{
flag[map[i][j]]=true;
sum++;
}
return sum;
}
int getCnt(int color) //对剩余的格子染color色,返回最多染色的个数(判断颜色color是否可以染)
{
int sum=0;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
{
if(map[i][j]==color&&status[i][j]==2) //找到一个与联通块相邻且为color色的格子
{
flood(i,j,color);
sum++;
}
}
return sum;
}
bool IDAstar(int step) //IDA*迭代深搜
{
if(step==depth) //到达指定深度,且棋盘颜色全部一样
return h()==0;
if(step+h()>depth) return false; //如果预计要走的步数比指定深度大,不用接着深搜了
for(int color=0;color<6;color++)
{
int cpy[MAXN][MAXN];
memcpy(cpy,status,sizeof(status));
if(!getCnt(color)) //所有格子都染不了色
continue;
if(IDAstar(step+1)) return true;
memcpy(status,cpy,sizeof(cpy));
}
return false;
}
int main()
{
while(1)
{
memset(status,0,sizeof(status));
memset(map,0,sizeof(map));
scanf("%d",&N);
if(!N) return 0;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%d",&map[i][j]);
flood(1,1,map[1][1]);
depth=h();
while(1)
{
if(IDAstar(0))
break;
depth++;
}
printf("%d\n",depth);
}
return 0;
}
Description
At the Institution for Bits and Bytes at University of Ramville, Prof. Jeremy Longword and his eight graduate students are investigating a brand new way of storing and manipulating data on magnetic disks for use in hard drives. The method is based on letting quasimagnetic quantum operations operate on the sectors on the disk, and is, of course, safer andmore reliable than any earlier invented storage method. The use of each quantum operation costs a certain amount of energy, and the more energy the storage unit consumes, the warmer it will get. Therefore, you and your research team, are assigned the task of writing a program that, given sets of possible quantum operations and their costs, can calculate the lowest possible total cost for transforming a set of data to the wanted result.
On the disk, binary words of length 1 ≤ L ≤ 20 are treated. The quantum operations are defined by strings of the same length as the binary words, and are built from the four lettersN (does nothing),F (inverts one bit),S (sets a bit to 1), andC (resets a bit to 0). Each letter in the string corresponds to an operation on the bit in the binary word at the same position. The binary words are transformed one by one and the total energy cost for the transformation is calculated as the sum of the costs for the performed quantum operations.
Input
The input starts with a single positive integer N ≤ 20 on a row, deciding the number of test cases that will follow. Then, for each of the test cases:
After this, nop rows follows, each of them containing the definition of a quantum operation followed by the energy cost 0 ≤ci 1000 of carrying out the quantum operation. The definition and the cost are separated by a single space.
Finally, there are nw rows, each containing two binary words separated by a single space. The first of these words should, when possible, be transformed to the second using the quantum operations. The binary words are expressed as sequences of 1’ s and 0’s. After these rows, the next test case follows, if there is any.
Output
Each test case should produce a row containing a list of the energy costs of transforming each of the binary words. The costs should be separated by a single space and presented in the same order as the corresponding input. When there is no successful way of transforming a binary word, “NP”, meaning not possible should be printed instead.
Sample Input
2 4 3 3 NFFN 1 NFNF 2 NNFN 4 0010 0100 0001 0010 0100 1000 4 4 5 CFSF 4 NNSS 3 FFFF 5 FNFN 6 1111 0000 1001 0110 0101 1000 1000 0011 0000 1001
Sample Output
1 3 NP 5 4 8 9 9
Source
以状态的花费为关键字构造一个降序的优先队列即可,每次从队首取出元素,根据题意模拟状态转移就行了,题目并不难,坑爹的是我把宏定义写错了,害得我一直在调试!
#include
#include
#include
#include
#include
#include