WaWa的奇妙冒险(第三周集训自闭现场)

第三周集训自闭现场(a* ida* dbfs真的好难)

(一)wlacm例题记录

A-有重复元素的排列问题 (水题,但卡我到自闭)

设R={ r1, r2 , …, rn}是要进行排列的n个元素。其中元素r1, r2 , …, rn可能相同。试设计一个算法,列出R的所有不同排列。
给定n 以及待排列的n 个元素。计算出这n 个元素的所有不同排列。

Input

输入数据的第1 行是元素个数n,1≤n≤500。接下来的1 行是待排列的n个元素。

Output

计算出的n个元素的所有不同排列并输出。文件最后1行中的数是排列总数。

Sample Input

4
aacc

Sample Output

aacc
acac
acca
caac
caca
ccaa
6

理解

	说实话,第一眼看到这题目第一反应是写一个类似  next_permutation()  的函数,进行判重来决定换位还是不换位,但换完顺序真的还是有问题(
	蒟蒻太菜了,真的没办法),后面换了一个思路,记录字母的数量,自己来拼串,这样就自动完成了去重,以及字典序升序的排序方式,虽然方法不那
	么巧妙,但已经是蒟蒻的极限了

AC代码

#include<iostream>
using namespace std;
int n,zm[26] = {0},sum = 0;
int a[550];
 
void dfs(int now){
    if(now == n){  ##  判断字母是否已被选完
        sum++;
        for(int i = 0;i < n;i++){
            printf("%c",a[i] + 'a');  ##  打印排列结果
        }
        printf("\n");
        return;
    }
     
    for(int i = 0;i < 26;i++){
        if(zm[i] > 0){
            a[now] = i;
            zm[i]--;
             
            dfs(now + 1);
             
            zm[i]++;
        }
    }
    return;
}
 
int main()
{
    cin >> n;
    string s;
    cin >> s;
    for(int i = 0;i < s.size();i++){
        zm[s[i] - 'a']++;  ##  统计字母出现次数
    }
    dfs(0);
    cout << sum << endl;
    return 0;
}

B-集合划分问题 (递推公式天下第一)

设S是一个具有n个元素的集合,S={a1,a2,……,an},现将S划分成k个满足下列条件的子集合S1,S2,……,Sk ,且满足:
在这里插入图片描述
则称S1,S2,……,Sk是集合S的一个划分。它相当于把S集合中的n个元素a1 ,a2,……,an 放入k个(0<k≤n<30)无标号的盒子中,使得没有一个盒子为空。请你确定n个元素a1 ,a2 ,……,an 放入k个无标号盒子中去的划分数S(n,k)。

Input

输入集合的元素个数n和划分的个数k

Output

输出划分数

Sample Input

23 7

Sample Output

4382641999117305

理解

	看到这题第一眼,这和组合数好像(狗头),考虑缩减规模找递推公式
	设k为划分集合数,n为元素数
	当k = 1时,只能划分一个集合 return 1
	当k = m时,只能划分成单个元素组成的集合 return 1
	当k > m时,不能划分集合 return 0
	
	接下来是缩减规模的方向,考虑(1)元素减1,划分数不变(2)元素减1,划分数减1
	(下面是网上偷的一段,便于理解)
	设n个元素的集合可以划分为F(n,k)个不同的由k个非空子集组成的集合。

	考虑3个元素的集合,可划分为
	① 1个子集的集合:{{1,2,3}}
	② 2个子集的集合:{{1,2},{3}},{{1,3},{2}},{{2,3},{1}}
	③ 3个子集的集合:{{1},{2},{3}}
	∴F(3,1)=1;F(3,2)=3;F(3,3)=1;

	如果要求F(4,2)该怎么办呢?

	A.往①里添一个元素{4},得到{{1,2,3},{4}}

	B.往②里的任意一个子集添一个4,得到
	{{1,2,4},{3}},{{1,2},{3,4}},
	{{1,3,4},{2}},{{1,3},{2,4}},
	{{2,3,4},{1}},{{2,3},{1,4}}

	∴F(4,2)=F(3,1)+2*F(3,2)=1+2*3=7
	
	可以推得递推公式为  F(n,k) = F(n-1,k-1) + k*F(n-1,k)

	后来学长也说了一下贝尔数,但有点看不懂,蒟蒻数学很差

原地址 https://blog.csdn.net/mikasa3/article/details/51283929

AC代码

#include<iostream>
using namespace std;
typedef long long ll;
 
ll f(ll n,ll k){
    if(n == k || k == 1) return 1;
     
    return f(n - 1,k - 1) + k*f(n - 1,k);
}
 
int main()
{
    ll n,k;
    cin >> n >> k;
    cout << f(n,k) << endl;
    return 0; 
}

C-简单背包问题 (if的巧妙运用)

简单的背包问题。设有一个背包,可以放入的重量为s。现有n件物品,重量分别为w1,w2…,wn,(1≤i≤n)均为正整数,从n件物品中挑选若干件,使得放入背包的重量之和正好为s。找到一组解即可。

如果找不到,输出"not found"(引号不输出)

Input

第一行是物品总件数和背包的载重量,第二行为各物品的重量。

Output

各所选物品重量(按编号从小到大顺序输出)

Sample Input

5 10
1 2 3 4 5

Sample Output

number:1 weight:1
number:4 weight:4
number:5 weight:5

理解

	刚开始做这题想用的是dp,只要看重量为s的背包里有没有重量的s的物品就好了,然而,他要求装了什么东西。。。瞬间自闭
	之后的思路是洛谷 “选数” 的思路,取到了保存路径就ok,反正他只要一组,然后 wa33%
	最后看完大佬代码,理解了一下,发现这道题取与不取(二叉树)写法之后,写了一个类似的,终于成功ac
	其实看到二叉树本来是想开一个01数组去拼的(当时还没学位运算枚举),但发现大佬代码比这强很多,默默偷走(大佬牛逼)

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000];
  
int dfs(int sum,int now){  ##  从后往前dfs是为了输出的时候为升序
    if(sum == m) return 1;
    if(sum > m || now < 1) return 0;
    if(dfs(sum + a[now],now - 1)){  ## 利用dfs搜完一条路才会搜其他路的性质,完美地利用if完成了输出工作
        printf("number:%d  weight:%d\n",now,a[now]);
        return 1;
    }
    else return dfs(sum,now - 1);
}
 
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++) cin >> a[i];
    if(!dfs(0,n)) cout << "not found" << endl;  ##  如果全都没搜到,输出not found
    return 0;
}

因为初学dfs,理解还很浅薄,这道题if的用法,感觉就像给当时的我开了新的大门,算是对dfs的搜索原理理解更深了一点吧

D-双色Hanoi塔问题 (水题? ac的莫名其妙)

设A、B、C是3 个塔座。开始时,在塔座A 上有一叠共n 个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为1,2,……,n,奇数号圆盘着蓝色,偶数号圆盘着红色,如图所示。现要求将塔座A 上的这一叠圆盘移到塔座B 上,并仍按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则(1):每次只能移动1 个圆盘;

   规则(2):任何时刻都不允许将较大的圆盘压在较小的圆盘之上;

   规则(3):任何时刻都不允许将同色圆盘叠在一起;

   规则(4):在满足移动规则(1)-(3)的前提下,可将圆盘移至A,B,C 中任一塔座上。

对于给定的正整数n,编程计算最优移动方案
在这里插入图片描述

Input

输入正整数n。

Output

将计算出的最优移动方案输出。每一行由一个正整数k和2个字符c1和c2组成,表示将第k个圆盘从塔座c1移到塔座c2上。2个字符之间有1个空格。

Sample Input

3

Sample Output

1 A B
2 A C
1 B C
3 A B
1 C A
2 C B
1 A B

理解

	当时真的没懂这题,因为单色汉诺塔都是懵懵懂懂的,姑且拿小规模的双色在纸上模拟了一遍,枚举到4发现,貌似和单色汉诺塔没啥不同
	就大胆做了一个尝试,写了个单色汉诺塔的 a柱移b柱 发现就a掉了

AC代码

#include<iostream>
using namespace std;
 
void hanoi(int n,char a,char b,char c){
    if(n == 0) return;
    hanoi(n - 1,a,c,b);
    printf("%d %c %c\n",n,a,b);
    hanoi(n - 1,c,b,a);
    return;
}
 
int main()
{
    int n;
    cin >> n;
    hanoi(n,'A','B','C');
    return 0;
}

E-蒜头君的生日 (基姆拉尔森计算公式)

蒜头君的生日快到了,蒜头君希望是在周末,蒜头君请你帮忙算出他生日在星期几。

Input

输入三个正整数,分别表示年、月、日。保证输入年份合法。

Output

输出星期几。用 Monday、Tuesday、Wednesday、Thursday、Friday、Saturday、Sunday 表示星期几。

Sample Input

1 1 1

Sample Output

Monday

理解

	没啥好讲的,记录这题就是记录一下基姆拉尔森计算公式
	
	W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7
	在公式中d表示日期中的日数,m表示月份数,y表示年数。
	注意:在公式中有个与其他公式不同的地方:
	把一月和二月看成是上一年的十三月和十四月,例:如果是2004-1-10则换算成:2003-13-10来代入公式计算。

AC代码

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    string s[10] = {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
    int y,m,d;
    cin >> y >> m >> d;
    if(m == 1 || m == 2){
        m += 12;
        y -= 1;
    }
     
    int w = (d + 2*m + 3*(m + 1)/5 + y + y/4 - y/100 + y/400) % 7;
    cout << s[w] << endl;
    return 0;
}

F-节假日 (水题,测试样例有问题)

日历有阳历(公历)和阴历(农历)之分。每年都有法定节假日,这些分成三类——双休、阳历节假日、阴历节假日
1.双休
1)周六和周日2天
2.阳历节假日
1)元旦:阳历每年1月1日,放假1天
2)劳动节:阳历每年5月1日,放假1天
3)国庆节:阳历每年10月1日,放假3天
4)圣诞节:阳历每年12月25日,放假1天
3.阴历节假日
1)春节:阴历每年1月1日,放假3天
2)清明节:阳历每年4月4-6日之间的某天,放假1天
3)端午节:阴历每年5月5日,放假1天
4)中秋节:阴历每年8月15日,放假1天
当节假日和双休重合时,双休不延后也不提前,保证节假日之间不会重合。现在给你某年的所有阴历节假日的阳历日期,以及当年的1月1日是星期几,请你计算出这一年(阳历1月1日到12月31日)放了多少天假(包括双休、阳历节假日和阴历节假日)。

Input

第一行输入年份y(1900<y≤2050)。
接下来4行,每行输入两个整数m,d,依次表示春节、清明节、端午节和中秋节的阳历日期。
最后一行一个整数表示当年1月1号是星期几(一周内的第几天,每周从星期一开始计数,即星期一为第一天)。

Output

输出一个整数,表示当年放假的天数。

Sample Input

2017
1 28
4 4
5 30
10 4
7

Sample Output

113

理解

	思路是比较好像的,先统计一年里所有双休的天数,然后减去和双休重复的节假日就好了(基姆拉尔森计算公式)
	但这题测试样例有问题,不符合实际,导致部分人疯狂wa

AC代码

#include <bits/stdc++.h>
using namespace std;
 
int a[4][4] = {0};
 
int year(int x){  ##  判断是否为闰年
    return (x % 400 == 0 || x % 4 == 0 && x % 100 != 0) ?1 :0;
}
 
int month(int y,int m){  ##  判断月份天数
    int k[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
    return (year(y) && m == 2) ?k[m] + 1 :k[m];
}
 
int xq(int y,int m,int d){  ##  基姆拉尔森计算公式计算该日期为星期几
    if(m == 1 || m == 2){
        m += 12;
        y -= 1;
    }
    int w = (d + 2*m + 3*(m + 1)/5 + y + y/4 - y/100 + y/400) % 7 + 1;
    return w;
}
 
int cf(int y,int m,int d,int s){  ##  判重
    int sum = 0;
    for(int i = 0;i < s;i++){
        int k = xq(y,m,d);
        if(k == 7 || k == 6) sum++;
        d++;
        if(d > month(y,m)){
            d = 1;
            m++;
        }
    }
    return sum;
}
 
void cal(int y,int ds){
    int sum = 0,m = 1,d = 1;
    while(d < 32 && m < 13){  ##  得出一年的双休天数
        if(ds == 7 || ds == 6) sum++;
         
        d++;ds++;
        if(ds > 7) ds -= 7;
        if(d > month(y,m)){
            d = 1;
            m++;
        }
    }
     
    sum += 12;  ##  处理节假日和双休重复的情况
    sum -= cf(y,1,1,1);
    sum -= cf(y,5,1,1);
    sum -= cf(y,10,1,3);
    sum -= cf(y,12,25,1);
    sum -= cf(y,a[0][0],a[0][1],3);
    sum -= cf(y,a[1][0],a[1][1],1);
    sum -= cf(y,a[2][0],a[2][1],1);
    sum -= cf(y,a[3][0],a[3][1],1);
    cout << sum << endl;
    return;
}
 
int main()
{
    int y,ds;
    cin >> y;
    for(int i = 0;i < 4;i++){
        cin >> a[i][0] >> a[i][1];
    }
    cin >> ds;
    cal(y,ds);
    return 0;
}

G-幼儿园买玩具 (水题,位运算枚举)

蒜厂幼儿园有n个小朋友,每个小朋友都有自己想玩的玩具。身为幼儿园园长的你决定给幼儿园买一批玩具,由于经费有限,你只能买mm个玩具。已知玩具商店一共卖k种玩具,编号为1,2,3,…k,你让每个小朋友把想玩的玩具编号都写在了纸上。你希望满足尽可能多的小朋友的需求,请计算出最多能满足多少个小朋友的玩具需求

Input

第一行,输入三个整数n,m,k(1≤n≤100,1≤m≤k≤15),中间用空格分开
接下来n行,第i+1(0≤i<n)行的第一个数字a_i代表第i个小朋友想玩的玩具数量,
接下来有a_i个数字,代表这a_i个玩具的编号。

Output

输出一个整数,表示最多能满足多少小朋友的玩具需求。

Sample Input

5 3 5
2 1 4
0
2 3 1
3 2 3 4
2 4 5

Sample Output

3

理解

	其实这题也是水题,但位运算讲的很浅,只讲了枚举这一块,姑且拿一道难一点的记录一下(其他比这个简单的多)

AC代码

#include <bits/stdc++.h>
using namespace std;
 
struct xpy{  记录小朋友喜欢的玩具(此处其实可以用vector,更方便一点)
    int num;
    int x[20];
} a[105];
 
int main()
{
    int n,m,k,ans = 0;
    cin >> n >> m >> k;
    for(int i = 0;i < n;i++){
        cin >> a[i].num;
        for(int j = 0;j < a[i].num;j++){
            cin >> a[i].x[j];
        }
    }
     
    for(int i = 0;i < (1 << k);i++){
        int sum = 0;
        for(int j = 0;j < k;j++){
            if(i & (1 << j)) sum++;
        }
         
        if(sum == m){  ##  找到只买了m件玩具的枚举
            int sum2 = 0;
            for(int j = 0;j < n;j++){
                int f = 1;  ##  假设刚开始小朋友是满足的,解决无要求的孩子
                for(int k = 0;k < a[j].num;k++){
                    if(!(i & (1 << a[j].x[k] - 1))){  ##  看小孩子喜欢的这件玩具是否在购买的玩具内
                        f = 0;
                        break;
                    }
                }
                if(f) sum2++;  ##  满足的小朋友的数量
            }
            if(sum2 > ans) ans = sum2;
        }
    }
    cout << ans << endl;
    return 0;
}

H-蚂蚁感冒 (思维题,这个搜索不会写)

长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。 每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

Input

第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。

接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。

Output

要求输出1个整数,表示最后感冒蚂蚁的数目。

Sample Input

【测试样例1】
3
5 -2 8
【测试样例2】
5
-10 8 -20 12 25

Sample Output

【测试样例1】
1
【测试样例2】
3

理解

	这题刚开始想着以为是搜索,发现完全想不到怎么搜索,然后找了一下规律,发现完全用不上搜索
	以一个从左往右走的蚂蚁为例,他只有可能感染两个群体
	(1)在他右边所有往左走的蚂蚁
	(2)在他左边所有往右走的蚂蚁(注:需要他的右边有往左走的蚂蚁被感染才能出现第二个感染群体)
	用左往右走也是类似情况,所以遍历两边就ac了

AC代码

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    int n;
    while(cin >> n){
        int a[n],sum = 0;
        for(int i = 0;i < n;i++) cin >> a[i];
        
        for(int i = 0;i < n;i++){  ## 第一遍遍历,找往右走蚂蚁右边往左走的蚂蚁  或者  往左走蚂蚁左边往右走的蚂蚁
            if(a[0] > 0 && -a[i] > a[0]) sum++;
            if(a[0] < 0 && a[i] > 0 && a[i] < -a[0]) sum++;
        }
         
        if(sum){
            for(int i = 0;i < n;i++){  ##  如果上一个遍历里面有被感染的蚂蚁,再统计第二情况下被感染的蚂蚁
                if(a[0] > 0 && a[i] > 0 && a[i] < a[0]) sum++;
                if(a[0] < 0 && a[i] < a[0]) sum++;
            }
        }
         
        cout << sum + 1 << endl;
    }
    return 0;
}

I-最佳调度问题 (dfs的剪枝艺术)

假设有n个任务由k个可并行工作的机器完成。完成任务i需要的时间为ti。试设计一个算法找出完成这n个任务的最佳调度,使得完成全部任务的时间最早。

对任意给定的整数n和k,以及完成任务i需要的时间为ti,i=1~n。编程计算完成这n个任务的最佳调度。

Input

给出输入数据。第一行有2 个正整数n和k。第2 行的n个正整数是完成n个任务需要的时间。

Output

将计算出的完成全部任务的最早时间输出

Sample Input

7 3
2 14 4 16 6 5 3

Sample Output

17

理解

	这道题是真的难,卡思路到死,后来问了大佬才知道怎么写,而且剪枝难度也是奇思妙想级别的(蒟蒻哭泣)
	大概思路就是,先搜出第一种结果的值,用其来做一个min值来剪枝后面找到的结果
	为了剪枝减的节点更高,先对所有的任务进行一个从大到小的排序,在进行dfs

AC代码

#include <bits/stdc++.h>
using namespace std;
 
int n,k,a[10000],b[10000];
int mins = 0x3f3f3f3f;  ##  刚开始mins设置最大,方便后面找到的进行一个替换
 
bool comp(int x,int y){
    return x > y;
}
 
void dfs(int now,int sum){
    if(sum >= mins) return;  ##  假设这种分配方案带来的sum已经大于之前方案的mins,直接返回(剪枝)
    if(now == n){  ##  n个任务都分配完了,判断是否比mins来的小,如果小就更行mins,其实这里可以不用判断,直接更新,因为大于等于已经全部被剪掉了
        mins = min(sum,mins);
        return;
    }
     
    for(int i = 0;i < k;i++){  ##  k个容器来装
        if(b[i] + a[now] <= mins){  ##  如果这么装已经比之前找到的最小值来的大了,就没有必要再尝试这种方案
            b[i] += a[now];
            dfs(now + 1,max(b[i],sum));
            b[i] -= a[now];
        }
    }
    return;
}
 
int main()
{
    cin >> n >> k;
    for(int i = 0;i < n;i++) cin >> a[i];
    sort(a,a+n,comp);  ##  从大到小排序本质目的是为了对后面的尝试在更高的结点剪掉,减少更多不必要的搜索
    dfs(0,0);
    cout << mins << endl;
    return 0;
}

J-拆分自然数 (dfs)

任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。

当n=7共14种拆分方法:

7=1+1+1+1+1+1+1

7=1+1+1+1+1+2

7=1+1+1+1+3

7=1+1+1+2+2

7=1+1+1+4

7=1+1+2+3

7=1+1+5

7=1+2+2+2

7=1+2+4

7=1+3+3

7=1+6

7=2+2+3

7=2+5

7=3+4

Input

输入自然数n

Output

输出拆分的方案。

Sample Input

7

Sample Output

1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4

理解

	刚开始这题写到心态爆炸,划分是简单的,但弄成字典升序是真的难
	后面想到的思路类似拆分的写法加上最后的数字应当大于等于之前的数字,完成了ac

AC代码

#include <bits/stdc++.h>
using namespace std;
  
int n,sum = 0;
int ans[1000] = {0},q = 0;
  
void dfs(int now){
    if(now == 0){  ##  当前数字已经被分完,输出找到的结果
        printf("%d",ans[1]);
        for(int i = 2;i <= q;i++) printf("+%d",ans[i]);
        printf("\n");
        return;
    }
      
    for(int i = 1;i < n;i++){
        if(ans[q] <= i && now - i >= 0){  ##  当前值与前者比较,大于前者并且小于剩余的值则继续dfs
            q++;
            ans[q] = i;
              
            dfs(now - i);
              
            ans[q] = 0;
            q--;
        }
    }
    return;
}
  
int main()
{
    cin >> n;
    dfs(n);
    return 0;
}

K-划分整数 (递推公式天下第一)

将整数n分成k份,且每份不能为空,任意两种分法不能相同(不考虑顺序)。例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5; 1,5,1; 5,1,1;

问有多少种不同的分法。

Input

输入n,k (6<n≤200,2≤k≤6)

Output

输出一个整数,即不同的分法。

Sample Input

7 3

Sample Output

4

理解

	看到题目第一反应,又是一个递推公式,莫名想到当初写的 “分梨” 【狗头】
	这道题大概也能看做没有空盘的分梨
	所以大概思路就比较好理了
	k = 1,return 1
	n = k,return 1
	k > n,return 0

	因为当做分梨来看,我们可以缩小两个方向的规模
	F(n-1,k-1) 因为少一个盘子和一个梨,只要把梨加到多出来的空盘子里就好了
	F(n-k,k) 盘子没少,但少了梨,因为你不能出现重复的组合,所以你要把每盘都加一个梨,不改变原状态
	得到公式F(n,k) = F(n-1,k-1) + F(n-k,k)

AC代码

#include <bits/stdc++.h>
using namespace std;
 
int f(int n,int k){
    if(k == 1 || n == k) return 1;
    if(k > n) return 0;
     
    return f(n - 1,k - 1) + f(n - k,k);
}
 
int main()
{
    int n,k;
    cin >> n >> k;
    cout << f(n,k) << endl;
    return 0;
}

L-素数环问题 (dfs)

素数环:从1到n(1<=n<=12)这n个数摆成一个环,要求相邻的两个数的和是一个素数。

请输出最多前5个答案以及方案总数。

如果无解,输出"no solution!"(引号不输出)

Input

输入n

Output

请输出最多前5个答案(不足5个按实际答案数输出) 以及方案总数。

如果无解,输出"no solution!"(引号不输出)

Sample Input

10

Sample Output

<1>1 2 3 4 7 6 5 8 9 10
<2>1 2 3 4 7 10 9 8 5 6
<3>1 2 3 4 9 8 5 6 7 10
<4>1 2 3 8 5 6 7 4 9 10
<5>1 2 3 8 5 6 7 10 9 4
960

理解

	一道中规中矩的dfs题目,剪枝也不难,但没全局变量的初始化卡了一年(貌似oj无视了全局变量里面的初始化导致我一直wa)
	大体思路:
	(1)打表判断两数和是否为素数
	(2)奇数全部剪枝(1除外),因为奇数情况下必然有两个奇数相连,而素数里面只有2为偶数
	其他的dfs搜就完事了

AC代码

#include <bits/stdc++.h>
using namespace std;
  
int n,sum = 0;
int a[100] = {0};
int b[100] = {0};
int ans[20] = {0};
int vis[20] = {0};
  
void ss(){  ##  欧拉筛打表
    for (int i = 2;i <= 100; i++) {
        if (!a[i]) {
            b[++b[0]] = i;
        }
        for (int j = 1; j <=b[0] && i*b[j] <= 100; j++) {
            a[i*b[j]] = 1;
            if (i % b[j] == 0) {
                break;
            }
        }
    }
}
  
void dfs(int now){
    if(now > n && !a[ans[n] + ans[1]]){  ##  注意是环,首尾和也要判断
        sum++;
        if(sum <= 5){
            printf("<%d>",sum);
            for(int i = 1;i <= n;i++) printf("%d ",ans[i]);
            printf("\n");
        }
        return;
    }
      
    for(int i = 1;i <= n;i++){
        if(!vis[i]){
            if(now == 1 || (now >= 2 && !a[i + ans[now - 1]])){
                vis[i] = 1;
                ans[now] = i;
              
                dfs(now + 1);
              
                ans[now] = 0;
                vis[i] = 0;
            }
        }
    }
    return;
}
  
int main()
{
    ss();
    cin >> n;
    if((n % 2) && n != 1) cout << "no solution!" << endl;  ##  1排除在外,因为1 + 12,是素数
    else{
        sum = 0;  ##  一定要初始化,被这个坑自闭了
        dfs(1);
        if(sum) cout << sum << endl;
    }
    return 0;
}

M-面积 (bfs)

编程计算由“”号围成的下列图形的面积。面积计算方法是统计号所围成的闭合曲线中水平线和垂直线交点的数目。如下图所示,在1010的二维数组中,有“”围住了15个点,因此面积为15。

0 0 0 0 0 0 0 0 0 0

0 0 0 0 * * * 0 0 0

0 0 0 0 * 0 0 * 0 0

0 0 0 0 0 * 0 0 * 0

0 0 * 0 0 0 * 0 * 0

0 * 0 * 0 * 0 0 * 0

0 * 0 0 * * 0 * * 0

0 0 * 0 0 0 0 * 0 0

0 0 0 * * * * * 0 0

0 0 0 0 0 0 0 0 0 0

Sample Input

0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 0 0 0
0 0 0 0 1 0 0 1 0 0
0 0 0 0 0 1 0 0 1 0
0 0 1 0 0 0 1 0 1 0
0 1 0 1 0 1 0 0 1 0
0 1 0 0 1 1 0 1 1 0
0 0 1 0 0 0 0 1 0 0
0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0

Sample Output

15

理解

	这道题也是不难,之前因为bfs和dfs没学且不熟,在上一周真写不了这个的搜索
	这周写这题就很轻松了,大概思路就是把外层的0涂成1,然后统计剩下的0的数量
	不过要注意开始涂色的坐标,为了防止外圈1,我的思路是在外面再包一层0

AC代码

#include <bits/stdc++.h>
using namespace std;
 
typedef pair<int,int> P;  因为是坐标,用pair就很舒服
int vis[12][12];
int mvx[4] = {-1,0,0,1};
int mvy[4] = {0,1,-1,0};
 
int check(int x,int y){
    if(x < 0 || x > 11 || y > 11 || y < 0 || vis[x][y]) return 0;  ##  外面涂一层所以边界范围认真考虑,没考虑好这个wa到自闭
    return 1;
}
 
void bfs(int x,int y){
    queue <pair<int,int> > s;
    vis[x][y] = 1;
     
    P temp(x,y);
    s.push(temp);
     
    while(!s.empty()){
        P point = s.front();s.pop();
         
        for(int i = 0;i < 4;i++){
            int tx = point.first + mvx[i],ty = point.second + mvy[i];
            if(check(tx,ty)){
                P tp(tx,ty);
                s.push(tp);
                vis[tx][ty] = 1;  ##  图,这里也是根据条件进行涂色操作
            }
        }
    }
    return;
}
 
int main()
{
    memset(vis,0,sizeof(vis));
    int sum = 0;
     
    for(int i = 1;i <= 10;i++){
        for(int j = 1;j <= 10;j++){
            scanf("%d",&vis[i][j]);
        }
    }
     
    bfs(0,0);
     
    for(int i = 1;i <= 10;i++){
        for(int j = 1;j <= 10;j++){
            if(!vis[i][j]) sum++;
        }
    }
    cout << sum << endl;
}

N-最少转弯问题 (bfs)

给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山。平地可以通过,高山则不能。现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯才能到达目的地(x2,y2)?你只能沿着水平和垂直方向的平地上行进,拐弯次数就等于行进方向的改变(从水平到垂直或从垂直到水平)的次数。例如:如图,最少的拐弯次数为5。
在这里插入图片描述

Input

第1行:n m

第2至n+1行:整个地图地形描述(0:空地;1:高山),

如(图)第2行地形描述为:1 0 0 0 0 1 0

           第3行地形描述为:0 0 1 0 1 0 0

           ……

第n+2行:x1 y1 x2 y2 (分别为起点、终点坐标)

Output

输出s (即最少的拐弯次数)

Sample Input

5 7
1 0 0 0 0 1 0
0 0 1 0 1 0 0
0 0 0 0 1 0 1
0 1 1 0 0 0 0
0 0 0 0 1 1 0
1 3 1 7

Sample Output

5

理解

	在做这道题之前,我是已经码了一遍vj上的连连看,所以最开始的思路就是推步数,记录方向,当方向不同时记录拐弯次数(刚开始当做-1处理,且其
	不存在回头这种操作,所以我们可以用i直接当方向使用)
	使用这个想法时也进行了思考,拐弯次数多的情况下,其步数(默认先进的先出,有一个步数的效果)必然比拐弯次数少的情况来的大(个人想法,不
	确定)
	这个想法是成功ac了,但学长貌似说样例设置不太好,没有考虑最小值情况直接输出第一个找到的就能ac?
	所以后面看了大佬的代码,写了第二个思路的写法,记录拐弯次数,按拐弯次数的递增构架bfs,这种思路应该是能解决找最小值的问题的,也ac了

AC代码

第一种想法,记录转弯次数和方向

#include <bits/stdc++.h>
using namespace std;
   
int m,n,vis[1010][1010],ans = 0x3f3f3f3f;
int mvx[4] = {-1,0,0,1};
int mvy[4] = {0,1,-1,0};
int endx,endy;
   
struct node{
    int a,b;
    int step;  ##  转弯次数
    int dir;  ##  方向
};
   
int check(int x,int y){
    if(x < 1 || x > m || y > n || y < 1 || vis[x][y]) return 0;
    return 1;
}
   
void bfs(int x,int y){
    queue <node> s;
    vis[x][y] = 1;
       
    node temp;
    temp.a = x;temp.b = y;temp.step = -1;temp.dir = -1;
    s.push(temp);
       
    while(!s.empty()){
        node point = s.front();s.pop();
           
        if(point.a == endx && point.b == endy){
            ans = point.step;
            return;
        }
        for(int i = 0;i < 4;i++){
            int tx = point.a + mvx[i],ty = point.b + mvy[i];
               
            if(check(tx,ty)){
                vis[tx][ty] = 1;
                node temp2;
                temp2.a = tx;
                temp2.b = ty;
                if(point.dir != i){  ##  不同则转弯次数加1
                    temp2.dir = i;
                    temp2.step = point.step + 1;
                }
                else{  ##  相同则转弯次数不变
                    temp2.dir = i;
                    temp2.step = point.step;
                }
                   
                s.push(temp2);
            }
        }
    }
    return;
}
   
int main()
{
    memset(vis,0,sizeof(vis));
    ans = 0x3f3f3f3f;
    cin >> m >> n;
       
    for(int i = 1;i <= m;i++){
        for(int j = 1;j <= n;j++){
            scanf("%d",&vis[i][j]);
        }
    }
       
    int x,y;
    cin >> x >> y >> endx >> endy;
       
    bfs(x,y);
    cout << ans << endl;
    return 0;
}

第二种写法(这种肯定对的)

#include <bits/stdc++.h>
using namespace std;
  
int m,n,vis[1010][1010],ans = 0x3f3f3f3f;
int mvx[4] = {-1,0,0,1};
int mvy[4] = {0,1,-1,0};
int endx,endy;
  
struct node{
    int a,b;
    int step;  ##  单纯记录转弯次数
};
  
int check(int x,int y){
    if(x < 1 || x > n || y > n || y < 1 || vis[x][y]) return 0;
    return 1;
}
  
void bfs(int x,int y){
    queue <node> s;
    vis[x][y] = 1;
      
    node temp;
    temp.a = x;temp.b = y;temp.step = -1;
    s.push(temp);
      
    while(!s.empty()){
        node point = s.front();s.pop();
          
        if(point.a == endx && point.b == endy){
            ans = point.step;
            return;
        }
          
        for(int i = 0;i < 4;i++){
            int tx = point.a + mvx[i],ty = point.b + mvy[i];
            while(check(tx,ty)){  ##  方向相同则无限走下去,直到到头
                vis[tx][ty] = 1;
                node temp2;
                temp2.a = tx;temp2.b = ty;temp2.step = point.step + 1;
                  
                s.push(temp2);
                tx += mvx[i];ty += mvy[i];
            }
        }
    }
    return;
}
  
int main()
{
    memset(vis,0,sizeof(vis));
    ans = 0;
    cin >> m >> n;
      
    for(int i = 1;i <= m;i++){
        for(int j = 1;j <= n;j++){
            scanf("%d",&vis[i][j]);
        }
    }
      
    int x,y;
    cin >> x >> y >> endx >> endy;
      
    bfs(x,y);
    cout << ans << endl;
    return 0;
}

(二)vjudge例题记录

A-Shredding Company (水题,dfs切段)POJ - 1416

You have just been put in charge of developing a new shredder for the Shredding Company Although a “normal” shredder would just shred sheets of paper into little pieces so that the contents would become unreadable, this new shredder needs to have the following unusual basic characteristics.

1.The shredder takes as input a target number and a sheet of paper with a number written on it.

2.It shreds (or cuts) the sheet into pieces each of which has one or more digits on it.

3.The sum of the numbers written on each piece is the closest possible number to the target number, without going over it.

For example, suppose that the target number is 50, and the sheet of paper has the number 12346. The shredder would cut the sheet into four pieces, where one piece has 1, another has 2, the third has 34, and the fourth has 6. This is because their sum 43 (= 1 + 2 + 34 + 6) is closest to the target number 50 of all possible combinations without going over 50. For example, a combination where the pieces are 1, 23, 4, and 6 is not valid, because the sum of this combination 34 (= 1 + 23 + 4 + 6) is less than the above combination’s 43. The combination of 12, 34, and 6 is not valid either, because the sum 52 (= 12 + 34 + 6) is greater than the target number of 50.
在这里插入图片描述
Figure 1. Shredding a sheet of paper having the number 12346 when the target number is 50

There are also three special rules :

1.If the target number is the same as the number on the sheet of paper, then the paper is not cut.

For example, if the target number is 100 and the number on the sheet of paper is also 100, then

the paper is not cut.

2.If it is not possible to make any combination whose sum is less than or equal to the target number, then error is printed on a display. For example, if the target number is 1 and the number on the sheet of paper is 123, it is not possible to make any valid combination, as the combination with the smallest possible sum is 1, 2, 3. The sum for this combination is 6, which is greater than the target number, and thus error is printed.

3.If there is more than one possible combination where the sum is closest to the target number without going over it, then rejected is printed on a display. For example, if the target number is 15, and the number on the sheet of paper is 111, then there are two possible combinations with the highest possible sum of 12: (a) 1 and 11 and (b) 11 and 1; thus rejected is printed. In order to develop such a shredder, you have decided to first make a simple program that would simulate the above characteristics and rules. Given two numbers, where the first is the target number and the second is the number on the sheet of paper to be shredded, you need to figure out how the shredder should “cut up” the second number.

Input

The input consists of several test cases, each on one line, as follows :

tl num1
t2 num2

tn numn
0 0

Each test case consists of the following two positive integers, which are separated by one space : (1) the first integer (ti above) is the target number, (2) the second integer (numi above) is the number that is on the paper to be shredded.

Neither integers may have a 0 as the first digit, e.g., 123 is allowed but 0123 is not. You may assume that both integers are at most 6 digits in length. A line consisting of two zeros signals the end of the input.

Output

For each test case in the input, the corresponding output takes one of the following three types :

sum part1 part2 …
rejected
error

In the first type, partj and sum have the following meaning :

1.Each partj is a number on one piece of shredded paper. The order of partj corresponds to the order of the original digits on the sheet of paper.

2.sum is the sum of the numbers after being shredded, i.e., sum = part1 + part2 +…

Each number should be separated by one space.
The message error is printed if it is not possible to make any combination, and rejected if there is
more than one possible combination.
No extra characters including spaces are allowed at the beginning of each line, nor at the end of each line.

Sample Input

50 12346
376 144139
927438 927438
18 3312
9 3142
25 1299
111 33333
103 862150
6 1104
0 0

Sample Output

43 1 2 34 6
283 144 139
927438 927438
18 3 3 12
error
21 1 2 9 9
rejected
103 86 2 15 0
rejected

理解

	也是一道比较简单的dfs,写的时候顺手就剪枝了,所以没感到啥压力
	题目大意:
	分割纸上的数字,找出他们加起来小于等于之前输入值的最大值

AC代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

int len = 0,f = 0,maxs = 0;
int n,ans[100],q = 0;
int ansk[100],l = 0;
string s;

void dfs(int now,int sum){	
	if(sum > n) return;  ##  如果累加和已经大于要求就剪掉,不再继续
	if(now > len - 1){
		if(sum == maxs) f = 1;  ##  如果最大值重复,f为真
		if(sum > maxs){
			maxs = sum;
			for(int i = 0;i < q;i++){
				ansk[i] = ans[i];
			}
			l = q;
			f = 0;  ##  更新新的最大值的时候要把f重新变成假
		}
	}
	
	int x = 0;
	for(int i = now;i < len;i++){
		x = x*10 + s[i] - '0';  ##  截取片段
		if(x <= n){  ##  比要求的值小才能加到sum里
			ans[q] = x;  ##  储存结果(截出来的一个个小片段)
			q++;
			
			dfs(i + 1,sum + x);
			
			q--;
			ans[q] = 0;
		}
	}
	return;
}

int main()
{	
	while(cin >> n >> s && n && s != "0"){
		memset(ans,0,sizeof(ans));
		memset(ansk,0,sizeof(ansk));
		len = s.size();
		maxs = 0;
		
		int sum = 0;
		for(int i = 0;i < len;i++){
			sum += s[i] - '0';
		}
		if(sum > n){  ##  剪枝,刚开始各位相加要是比n还来的大,那么就是error
			cout << "error" << endl;
			continue;
		}
		else{
			dfs(0,0);
			if(f) cout << "rejected" << endl;  ##  重复则输出rejected
			else{
				cout << maxs;
				for(int i = 0;i < l;i++) cout << ' ' << ansk[i];
				cout << endl;
			}
		}
	}
    return 0;
}

B-Sudoku (巧妙的标记法,dfs)POJ - 2676

Sudoku is a very simple task. A square table with 9 rows and 9 columns is divided to 9 smaller squares 3x3 as shown on the Figure. In some of the cells are written decimal digits from 1 to 9. The other cells are empty. The goal is to fill the empty cells with decimal digits from 1 to 9, one digit per cell, in such way that in each row, in each column and in each marked 3x3 subsquare, all the digits from 1 to 9 to appear. Write a program to solve a given Sudoku-task.
在这里插入图片描述

Input

The input data will start with the number of the test cases. For each test case, 9 lines follow, corresponding to the rows of the table. On each line a string of exactly 9 decimal digits is given, corresponding to the cells in this line. If a cell is empty it is represented by 0.

Output

For each test case your program should print the solution in the same format as the input data. The empty cells have to be filled according to the rules. If solutions is not unique, then the program may print any one of them.

Sample Input

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

Sample Output

143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127

理解

	这道题实际上N皇后区别不大,就是判断该行、该列、该小格里的某个数字是否已经被使用过
	行和列的判断及其简单,这个不必说了
	小格的判断则可以套用一个很神奇的公式map[i/3*3 + y/3][x]
	在棋盘的涵盖范围为(0,0)到(8.8)时,可以正好得到9小格的范围

AC代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

int maps[10][10],f = 0;
int r[10][10],c[10][10],g[10][10];

void dfs(int x,int y){	
	if(f) return;
	if(x == 9){  ##  找到答案后打印一个即可
		f = 1;
		for(int i = 0;i < 9;i++){
			for(int j = 0;j < 9;j++){
				printf("%d",maps[i][j]);
			}
			printf("\n");
		}
		return;
	}
	
	if(maps[x][y]){  ##  这个位置有数字就跳过去填下一个空位
		if(y == 8) dfs(x+1,0);
		else dfs(x,y+1);
	}
	else{
		for(int i = 1;i <= 9;i++){
			if(!r[x][i] && !c[y][i] && !g[(x/3)*3 + y/3][i]){  ##  如果这个位置能填这个数字就填进去
				maps[x][y] = i;
				r[x][i] = c[y][i] = g[(x/3)*3 + y/3][i] = 1;
				
				if(y == 8) dfs(x+1,0);
				else dfs(x,y+1);
				
				maps[x][y] = 0;
				r[x][i] = c[y][i] = g[(x/3)*3 + y/3][i] = 0;
			}
		}
	}
	return;
}

int main()
{	
	int t;
    cin >> t;
    while(t--){
    	memset(maps,0,sizeof(maps));
    	memset(r,0,sizeof(r));
    	memset(c,0,sizeof(c));
    	memset(g,0,sizeof(g));
    	
    	int d;
    	for(int i = 0;i < 9;i++){
    		for(int j = 0;j < 9;j++){
    			scanf("%1d",&d);
    			maps[i][j] = d;
    			if(d) r[i][d] = c[j][d] = g[(i/3)*3 + j/3][d] = 1;  ##  预处理vis
			}
		}

		f = 0;
		dfs(0,0);
	}
    return 0;
}

C-Channel Allocation(四色定理天下第一)POJ - 1129

When a radio station is broadcasting over a very large area, repeaters are used to retransmit the signal so that every receiver has a strong signal. However, the channels used by each repeater must be carefully chosen so that nearby repeaters do not interfere with one another. This condition is satisfied if adjacent repeaters use different channels.

Since the radio frequency spectrum is a precious resource, the number of channels required by a given network of repeaters should be minimised. You have to write a program that reads in a description of a repeater network and determines the minimum number of channels required.

如果找不到,输出"not found"(引号不输出)

Input

The input consists of a number of maps of repeater networks. Each map begins with a line containing the number of repeaters. This is between 1 and 26, and the repeaters are referred to by consecutive upper-case letters of the alphabet starting with A. For example, ten repeaters would have the names A,B,C,…,I and J. A network with zero repeaters indicates the end of input.

Following the number of repeaters is a list of adjacency relationships. Each line has the form:

A:BCDH

which indicates that the repeaters B, C, D and H are adjacent to the repeater A. The first line describes those adjacent to repeater A, the second those adjacent to B, and so on for all of the repeaters. If a repeater is not adjacent to any other, its line has the form

A:

The repeaters are listed in alphabetical order.

Note that the adjacency is a symmetric relationship; if A is adjacent to B, then B is necessarily adjacent to A. Also, since the repeaters lie in a plane, the graph formed by connecting adjacent repeaters does not have any line segments that cross.

Output

For each map (except the final one with no repeaters), print a line containing the minumum number of channels needed so that no adjacent channels interfere. The sample output shows the format of this line. Take care that channels is in the singular form when only one channel is required.

Sample Input

2
A:
B:
4
A:BC
B:ACD
C:ABD
D:BC
4
A:BCD
B:ACD
C:ABD
D:ABC
0

Sample Output

1 channel needed.
3 channels needed.
4 channels needed.

理解

	大意,相邻的信号塔不能用同一种中继器,题中会告诉你哪些塔是相邻的
	看到这题第一反应是分集合,相邻的别分到一起就行了,后面想想实现起来很难
	换了个思维,相邻的信号塔不能涂同一种颜色就ok了
	因为先写的  “Black And White”  这一题,对四色问题有了了解(再上一次看见就是在《嫌疑人X的献身
	》里了,当时跑去查过四色问题)
	因为四色定理,颜色最多就四种,一定意义上完成了很大幅度的剪枝

AC代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

int n,maps[30][30],mins = 6;
int vis[30];

int check(int now,int col){  ##  检查前面已经涂色的且相邻的灯塔,是否有相同颜色
	for(int i = 1;i < now;i++){
		if(maps[now][i] && vis[i] == col) return 0;
	}
	return 1;
}

void dfs(int now){
	if(now > n){
		int max1 = 1;
		for(int i = 1;i <= n;i++){  ##  这里其实可以不写比较,第一个找到了应该就是最小值了
			if(vis[i] > max1) max1 = vis[i];  ##  因为四色定理,除非已经出现相邻的三色,不然不可能出现第四种颜色,所以只要在颜色里面取最大值就好了,后面变大的没第一次找到的小自然没了影响 这也是我说的为啥第一次找到的就是最小值的原因
		}
		if(max1 < mins) mins = max1;
		return;
	}
	
	for(int i = 1;i <= 4;i++){  ##  根据四色问题做的剪枝
		if(check(now,i)){
			vis[now] = i;
			
			dfs(now+1);
			
			vis[now] = 0;
		}
	}
	return;
}

int main()
{
    while(cin >> n && n){
    	memset(maps,0,sizeof(maps));
    	memset(vis,0,sizeof(vis));
    	mins = 6;
    	
    	for(int i = 0;i < n;i++){
    		string k;
    		cin >> k;
    		for(int j = 2;j < k.size();j++){
    			maps[k[0] - 'A' + 1][k[j] - 'A' + 1] = 1;  ##  预处理为数字,方便后面的check函数判断是否相邻
			}
		}
		dfs(1);
		if(mins == 1) printf("1 channel needed.\n");
		else printf("%d channels needed.\n",mins);
	}
    return 0;
}

D-连连看 (dfs,剪枝很难,ac的莫名其妙)HDU - 1175

“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。

Input

输入数据有多组。每组数据的第一行有两个正整数n,m(0<n<=1000,0<m<1000),分别表示棋盘的行数与列数。在接下来的n行中,每行有m个非负整数描述棋盘的方格分布。0表示这个位置没有棋子,正整数表示棋子的类型。接下来的一行是一个正整数q(0<q<50),表示下面有q次询问。在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第x2行y2列的棋子能不能消去。n=0,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

理解

	一道比较有意思的dfs,剪枝起来有难度
	大意:问你这两块能不能通过连连看的方式消除掉,有以下几个条件
	(1)线段只能转弯两次
	(2)线段不能从外面绕过去
	关于转弯两次可以用可以用记方向的方式解决,因为不可能往回走,所以当i发生变化时,方向必然已经发
	生了变化,且当两次转弯后,他只能往最后的方向一直走下去,看能不能到达终点
	
	这道题真的a的莫名其妙,剪枝的调节没怎么改,改着改着emm 就过了? 貌似多了一个回溯来着,本来想
	着找一条路就ok,不用写回溯来着。。。

AC代码

#include <bits/stdc++.h>
using namespace std;

int n,m,f = 0;
int maps[1005][1005],ss[1005][1005];
int mvx[4] = {1,0,0,-1};
int mvy[4] = {0,1,-1,0};
int endx = 0,endy = 0;

void dfs(int x,int y,int fx,int sum){
	if(f) return;
	if(x == endx && y == endy){  ##  如果到了终点,那就不用再找了
		f = 1;
		return;
	}
	
	for(int i = 0;i < 4;i++){
		int tx = x + mvx[i];
		int ty = y + mvy[i];
		
		if(tx < 1 || tx > n || ty < 1 || ty > m || ss[tx][ty] || (sum > 2 && i != fx)) continue;  ##  如果下标越界或者已经转弯两次,并且方向再次发生变化
		if(maps[tx][ty] == 0 || (tx == endx && ty == endy)){  ##  路为0或是终点都是可以走的
			ss[tx][ty] = 1;  ##  多次查询,不能动原地图,写个ss当vis用
			
			if(fx != i) dfs(tx,ty,i,sum + 1);
			else dfs(tx,ty,i,sum);
			
			ss[tx][ty] = 0;
		}
	}
	return;
}

int main()
{
	while(cin >> n >> m && n && m){
		memset(maps,0,sizeof(maps));
		
		for(int i = 1;i <= n;i++){
			for(int j = 1;j <= m;j++){
				scanf("%d",&maps[i][j]);
			}
		}
		
		int t;
		cin >> t;
		while(t--){
			int x1,y1;
			scanf("%d%d%d%d",&x1,&y1,&endx,&endy);
			
			f = 0;
			memset(ss,0,sizeof(ss));
			if(maps[x1][y1] && maps[endx][endy] && maps[x1][y1] == maps[endx][endy]) dfs(x1,y1,-1,0);  ##  方向刚开始当-1,因为刚开始不知道方向,必然要加1if(f) printf("YES\n");
			else printf("NO\n");
		}
	}
	return 0;
}

E-Black And White (dfs,巧妙的剪枝) HDU - 5113

In mathematics, the four color theorem, or the four color map theorem, states that, given any separation of a plane into contiguous regions, producing a figure called a map, no more than four colors are required to color the regions of the map so that no two adjacent regions have the same color.
— Wikipedia, the free encyclopedia

In this problem, you have to solve the 4-color problem. Hey, I’m just joking.

You are asked to solve a similar problem:

Color an N × M chessboard with K colors numbered from 1 to K such that no two adjacent cells have the same color (two cells are adjacent if they share an edge). The i-th color should be used in exactly c i cells.

Matt hopes you can tell him a possible coloring.

Input

The first line contains only one integer T (1 ≤ T ≤ 5000), which indicates the number of test cases.

For each test case, the first line contains three integers: N, M, K (0 < N, M ≤ 5, 0 < K ≤ N × M ).

The second line contains K integers c i (c i > 0), denoting the number of cells where the i-th color should be used.

It’s guaranteed that c 1 + c 2 + · · · + c K = N × M .

Output

For each test case, the first line contains “Case #x:”, where x is the case number (starting from 1).

In the second line, output “NO” if there is no coloring satisfying the requirements. Otherwise, output “YES” in one line. Each of the following N lines contains M numbers seperated by single whitespace, denoting the color of the cells.

If there are multiple solutions, output any of them.

Sample Input

4
1 5 2
4 1
3 3 4
1 2 2 4
2 3 3
2 2 2
3 2 3
2 2 2

Sample Output

Case #1:
NO
Case #2:
YES
4 3 4
2 1 2
4 3 4
Case #3:
YES
1 2 3
2 3 1
Case #4:
YES
1 2
2 3
3 1

理解

	题目是很好理解的,就是问你M*N的棋盘能不能用K种颜色(每种颜色有数量限制)涂满且无相同颜色相邻
	单纯写这题是很简单的,考虑一个取色就好了,但剪枝有点难,tle到爆炸后看了大佬代码才懂怎么剪枝
	
	下面假设一个3*3的棋盘
	只有两种涂色,我们可以发现,当一种颜色超过它一半棋盘的格数+1的时候,已经做不到相邻格子涂色不
	同了,以此类推,得到公式 color[i] > (n*m - sum + 1)/2)时进行剪枝

AC代码

#include <bits/stdc++.h>
using namespace std;

int n,m,k,f = 0;
int col[30],maps[7][7];

int check(int x,int y,int i){  ##  从左上往右下图,只用判断两个方向就好了
	if(x - 1 > 0 && maps[x-1][y] == i) return 0;
	if(y - 1 > 0 && maps[x][y-1] == i) return 0;
	return 1;
}

int check2(int x,int y,int sum){  ##  剪枝,根据color[i] > (n*m - sum + 1)/2)
	for(int i = 1;i <= k;i++){
		if(col[i] > (n*m - sum + 1)/2) return 1;
	}
	return 0;
}

void dfs(int x,int y,int sum){  ##  sum表示以及已经涂了多少格子
	if(f) return;
	if(check2(x,y,sum)) return;
	if(sum == m*n){
		f = 1;
		printf("YES\n");
		for(int i = 1;i <= n;i++){
			printf("%d",maps[i][1]);
			for(int j = 2;j <= m;j++){
				printf(" %d",maps[i][j]);
			}
			printf("\n");
		}
		return;
	}
	
	for(int i = 1;i <= k;i++){
		if(col[i] && check(x,y,i)){
			col[i]--;
			maps[x][y] = i;
			
			if(y == m) dfs(x + 1,1,sum + 1);
			else dfs(x,y + 1,sum + 1);
			
			col[i]++;
			maps[x][y] = 0;
		}
	}
	return;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
}

int main()
{
	int t;
	cin >> t;
	for(int g = 1;g <= t;g++){
		memset(col,0,sizeof(col));
		memset(maps,0,sizeof(maps));
		f = 0;
		
		cin >> n >> m >> k;
		for(int i = 1;i <= k;i++) cin >> col[i];
		
		printf("Case #%d:\n",g);
		dfs(1,1,0);
		if(!f) printf("NO\n");
	}
	return 0;
}

F-Find The Multiple (dfs) POJ - 1426

Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal representation contains only the digits 0 and 1. You may assume that n is not greater than 200 and there is a corresponding m containing no more than 100 decimal digits.

Input

The input file may contain multiple test cases. Each line contains a value of n (1 <= n <= 200). A line containing a zero terminates the input.

Output

For each value of n in the input print a line containing the corresponding value of m. The decimal representation of m must not contain more than 100 digits. If there are multiple solutions for a given value of n, any one of them is acceptable.

Sample Input

2
6
19
0

Sample Output

10
100100100100100100
111111111111111111

理解

	思路是比较好想的,找个能整除6的就行,根据取模运算
	刚开始自己写了bfs,然而疯狂tle???
	然后看了学长讲了dfs的写法,才会写,但还是没懂bfs为啥就tle了

AC代码

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

int n,f;

void dfs(long long x,int i){  ##  自己估算一个差不多的范围,然后dfs(后面学完ida*,感觉用ida*写更合适,但题写的太累了,之后有机会试试)
	if(f || i > 18) return;
	if(!(x % n)){
		f = 1;
		cout << x << endl;
	}
	dfs(x*10,i+1);
	dfs(x*10+1,i+1);
	return;
}

int main()
{
	while(cin >> n && n){
		f = 0;
		dfs(1,0);
	}
	return 0;
}

我tle的bfs

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

int n;

int check(string k){
	int sum = 0;
	for(int i = 0;i < k.size();i++){
		sum = (sum * 10 + k[i] - '0') % n;
	}
	if(!sum){
		cout << k << endl;
		return 1;
	}
	if(k.size() > 19) return 1;  强行加了长度限制,但貌似没啥用
	return 0;
}

void bfs(){
	queue <string> s;
	string temp = "";
	s.push(temp);
	
	while(!s.empty()){
		temp = s.front();s.pop();
		
		s.push(temp + "1");
		if(check(temp + "1")) return;
		
		if(!temp.size()) continue;
		
		s.push(temp + "0");
		if(check(temp + "0")) return;
	}
	return;
}

int main()
{
	while(cin >> n && n){
		bfs();
	}
	return 0;
}

后来学长提到了中国剩余定理(孙子问题)的解题方式,不过还没看(蒟蒻写题到自闭1555)

G-Pots (bfs,模拟)POJ - 3414

You are given two pots, having the volume of A and B liters respectively. The following operations can be performed:

    1.FILL(i) fill the pot i (1 ≤ i ≤ 2) from the tap;
    2.DROP(i) empty the pot i to the drain;
    3.POUR(i,j) pour from pot i to pot j; after this operation either the pot j is     full (and there may be some water left in the pot i), or the pot i is empty (and     all its contents have been moved to the pot j).

Write a program to find the shortest possible sequence of these operations that will yield exactly C liters of water in one of the pots.

Input

On the first and only line are the numbers A, B, and C. These are all integers in the range from 1 to 100 and C≤max(A,B).

Output

The first line of the output must contain the length of the sequence of operations K. The following K lines must each describe one operation. If there are several sequences of minimal length, output any one of them. If the desired result can’t be achieved, the first and only line of the file must contain the word ‘impossible’.

Sample Input

3 5 4

Sample Output

6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)

理解

	题目很好理解,两个壶相互倒以及加水和倒水的操作,使其中一个壶内的水达到要求要求的水量即可
	个人理解过来,就是把我们之前写的类似迷宫的写法(4种操作方式)改成题目中的六种操作即可
	因为上周写的大量的模拟题,这种题目的模拟写起来还是很简单的

AC代码

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

int x,y,k,f = 1;
int vis[105][105];
typedef pair<int,int> P;
string cmd[7] = {  ##  方便后面输出操作
	"FILL(1)",
	"FILL(2)",
	"POUR(1,2)",
	"POUR(2,1)",
	"DROP(1)",
	"DROP(2)"
};

struct node{  ##  结构体,记录操作序号和当前a,b壶的水量
	int a,b;
	int x[105];
	int step;
	node(int x,int y):a(x),b(y){}  ##  构造函数
};

void print(node ans){  ##  打印函数
	cout << ans.step << endl;
	for(int i = 0;i < ans.step;i++){
		cout << cmd[ans.x[i]] << endl;
	}
	return;
}

void bfs(){
	queue <node> s;
	node temp(0,0);
	temp.step = 0;
	memset(temp.x,0,sizeof(temp.x));
	s.push(temp);
	
	while(!s.empty()){
		temp = s.front();s.pop();
		
		if(temp.a == k || temp.b == k){  ##  如果达到条件,打印结果并结束
			f = 0;
			print(temp);
			return;
		}
		
		node temp2 = temp;
		if(temp.a < x){  ##  加水操作
			temp2.a = x;temp2.b = temp.b;
			if(!vis[x][temp.b]){
				vis[x][temp.b] = 1;
				temp2.x[temp.step] = 0;
				temp2.step = temp.step + 1;
				s.push(temp2);
			}
		}
		
		if(temp.b < y){  ##  同上
			temp2.a = temp.a;temp2.b = y;
			if(!vis[temp.a][y]){
				vis[temp.a][y] = 1;
				temp2.x[temp.step] = 1;
				temp2.step = temp.step + 1;
				s.push(temp2);
			}
		}
		
		if(temp.a && (temp.b < y)){  ##  a往b里面倒水
			if(temp.a > (y - temp.b)){
				temp2.b = y;
				temp2.a = temp.a - (y - temp.b);
			}
			else{
				temp2.a = 0;
				temp2.b = temp.b + temp.a;
			}
			if(!vis[temp2.a][temp2.b]){
				vis[temp2.a][temp2.b] = 1;
				temp2.x[temp.step] = 2;
				temp2.step = temp.step + 1;
				s.push(temp2);
			}
		}
		
		if(temp.b && (temp.a < x)){  ##  b往a里面倒水
			if(temp.b > (x - temp.a)){
				temp2.a = x;
				temp2.b = temp.b - (x - temp.a);
			}
			else{
				temp2.b = 0;
				temp2.a = temp.a + temp.b;
			}
			if(!vis[temp2.a][temp2.b]){
				vis[temp2.a][temp2.b] = 1;
				temp2.x[temp.step] = 3;
				temp2.step = temp.step + 1;
				s.push(temp2);
			}
		}
		
		if(temp.a){  ##  倒水
			temp2.a = 0;temp2.b = temp.b;
			if(!vis[temp2.a][temp2.b]){
				vis[temp2.a][temp2.b] = 1;
				temp2.x[temp.step] = 4;
				temp2.step = temp.step + 1;
				s.push(temp2);
			}
		}
		
		if(temp.b){  ##  同上
			temp2.b = 0;temp2.a = temp.a;
			if(!vis[temp2.a][temp2.b]){
				vis[temp2.a][temp2.b] = 1;
				temp2.x[temp.step] = 5;
				temp2.step = temp.step + 1;
				s.push(temp2);
			}
		}
	}
	return;
}

int main()
{
	memset(vis,0,sizeof(vis));
	vis[0][0] = 1;
	cin >> x >> y >> k;
	bfs();
	if(f) cout << "impossible" << endl;
	return 0;
}

H-Asteroids! (bfs) HDU - 1240

You’re in space.
You want to get home.
There are asteroids.
You don’t want to hit them.

Input

Input to this problem will consist of a (non-empty) series of up to 100 data sets. Each data set will be formatted according to the following description, and there will be no blank lines separating data sets.

A single data set has 5 components:

Start line - A single line, “START N”, where 1 <= N <= 10.

Slice list - A series of N slices. Each slice is an N x N matrix representing a horizontal slice through the asteroid field. Each position in the matrix will be one of two values:

‘O’ - (the letter “oh”) Empty space

‘X’ - (upper-case) Asteroid present

Starting Position - A single line, “A B C”, denoting the <A,B,C> coordinates of your craft’s starting position. The coordinate values will be integers separated by individual spaces.

Target Position - A single line, “D E F”, denoting the <D,E,F> coordinates of your target’s position. The coordinate values will be integers separated by individual spaces.

End line - A single line, “END”

The origin of the coordinate system is <0,0,0>. Therefore, each component of each coordinate vector will be an integer between 0 and N-1, inclusive.

The first coordinate in a set indicates the column. Left column = 0.

The second coordinate in a set indicates the row. Top row = 0.

The third coordinate in a set indicates the slice. First slice = 0.

Both the Starting Position and the Target Position will be in empty space.

Output

For each data set, there will be exactly one output set, and there will be no blank lines separating output sets.

A single output set consists of a single line. If a route exists, the line will be in the format “X Y”, where X is the same as N from the corresponding input data set and Y is the least number of moves necessary to get your ship from the starting position to the target position. If there is no route from the starting position to the target position, the line will be “NO ROUTE” instead.

A move can only be in one of the six basic directions: up, down, left, right, forward, back. Phrased more precisely, a move will either increment or decrement a single component of your current position vector by 1.

Sample Input

START 1
O
0 0 0
0 0 0
END
START 3
XXX
XXX
XXX
OOO
OOO
OOO
XXX
XXX
XXX
0 0 1
2 2 1
END
START 5
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
XXXXX
XXXXX
XXXXX
XXXXX
XXXXX
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
OOOOO
0 0 0
4 4 4
END

Sample Output

1 0
3 4
NO ROUTE

理解

	题意也是很简单的,大意就是走三维迷宫,唯一要注意的点是,因为迷宫是三维的,在填入时,要把z轴放
	在最外层(z轴增加最慢)

AC代码

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

int n,x,y,z,endx,endy,endz,f = 1;
int maps[11][11][11],vis[11][11][11];
int mvx[] = {1,0,0,0,0,-1};
int mvy[] = {0,1,-1,0,0,0};
int mvz[] = {0,0,0,1,-1,0};

struct node{
	int x,y,z;
	int step;
	node(int a,int b,int c):x(a),y(b),z(c){}
};

int check(int x,int y,int z){  ##  只是能不能到,此处应该无需再设一个maps,原图上改即可,当时写的时候习惯性开了个vis用(确认用maps就好,可以ac)
	if(x < 0 || x >= n || y < 0 || y >= n || z < 0 || z >= n || vis[x][y][z] || maps[x][y][z]) return 0;
	return 1;
}

void bfs(){
	queue <node> q;
	node point(x,y,z);
	point.step = 0;
	q.push(point);
	
	while(!q.empty()){
		point = q.front();q.pop();
		if(point.x == endx && point.y == endy && point.z == endz){
			cout << n << ' ' << point.step << endl;
			f = 0;
			return;
		}
		for(int i = 0;i < 6;i++){  ##  和走二维迷宫没啥区别,就多了两个方向而已
			node temp(point.x + mvx[i],point.y + mvy[i],point.z + mvz[i]);
			if(check(temp.x,temp.y,temp.z)){
				temp.step = point.step + 1;
				vis[temp.x][temp.y][temp.z] = 1;
				q.push(temp);
			}
		}
	}
	return;
}

int main()
{
	string s;
	while(cin >> s >> n){
		memset(maps,0,sizeof(maps));
		memset(vis,0,sizeof(vis));
		f = 1;
		
		char ch;
		for(int i = 0;i < n;i++){
			for(int j = 0;j < n;j++){
				getchar();
				for(int k = 0;k < n;k++){
					scanf("%c",&ch);
					if(ch == 'X') maps[j][k][i] = 1;  ##  处理X,方便后面check操作
				}
			}
		}
		
		cin >> x >> y >> z >> endx >> endy >> endz >> s;
		vis[x][y][z] = 1;
		bfs();
		if(f) cout << "NO ROUTE" << endl;
	}
	return 0;
}

I-Friend Chains (bfs)HDU - 4460

For a group of people, there is an idea that everyone is equals to or less than 6 steps away from any other person in the group, by way of introduction. So that a chain of “a friend of a friend” can be made to connect any 2 persons and it contains no more than 7 persons.
For example, if XXX is YYY’s friend and YYY is ZZZ’s friend, but XXX is not ZZZ’s friend, then there is a friend chain of length 2 between XXX and ZZZ. The length of a friend chain is one less than the number of persons in the chain.
Note that if XXX is YYY’s friend, then YYY is XXX’s friend. Give the group of people and the friend relationship between them. You want to know the minimum value k, which for any two persons in the group, there is a friend chain connecting them and the chain’s length is no more than k .

Input

There are multiple cases.
For each case, there is an integer N (2<= N <= 1000) which represents the number of people in the group.
Each of the next N lines contains a string which represents the name of one people. The string consists of alphabet letters and the length of it is no more than 10.
Then there is a number M (0<= M <= 10000) which represents the number of friend relationships in the group.
Each of the next M lines contains two names which are separated by a space ,and they are friends.
Input ends with N = 0.

Output

For each case, print the minimum value k in one line.
If the value of k is infinite, then print -1 instead.

Sample Input

3
XXX
YYY
ZZZ
2
XXX YYY
YYY ZZZ
0

Sample Output

2

理解

	题目大意是,找出最长的朋友链,我和这人之间隔了多少人,朋友链长度就是隔的人数加1,如果出现没法
	联系的朋友,就输出-1

AC代码

#include <iostream>
#include <queue>
#include <cstring>
#include <map>
#include <vector>
using namespace std;

int n;

vector <int> k[1005];  ##  用普通数组判断是否有关系会超时,看大佬用了vector就没问题
int dis[1005][1005];
map <string,int> id;
 
void bfs(int x){
    queue <int> q;
	dis[x][x] = 0;
    
    q.push(x);
    while(!q.empty()){
    	int temp = q.front();q.pop();	
		for(int i = 0;i < k[temp].size();i++){
    		if(dis[x][k[temp][i]] == -1){  ##  如果这两个人之间的距离还没被赋值过,就是上一层的距离+1
    			dis[x][k[temp][i]] = dis[x][temp] + 1;
    			q.push(k[temp][i]);
			}
		}
	}
	return;
}
 
int main()
{	
	while(cin >> n && n){
		memset(dis,-1,sizeof(dis));
		id.clear();
		for(int i = 0;i < n;i++) k[i].clear();
		
		string s,s2;
		for(int i = 0;i < n;i++){
			cin >> s;
			id[s] = i;
		}
		
		int t;
		cin >> t;
		while(t--){
			cin >> s >> s2;
			k[id[s]].push_back(id[s2]);
			k[id[s2]].push_back(id[s]);
		}
		
		for(int i = 0;i < n;i++) bfs(i);
		
		int f = 0,maxs = 0;
		for(int i = 0;i < n;i++){
			for(int j = i + 1;j < n;j++){  ##  找最长朋友链,如果发现没赋值过的两个人,就输出-1
				if(dis[i][j] == -1){
					f = 1;
					break;
				}
				else maxs = max(maxs,dis[i][j]);
			}
		}
		
		if(f) cout << -1 << endl;
		else cout << maxs << endl;
	}
	return 0;
}

J-Eight (a*,bfs)(POJ - 1077)

The 15-puzzle has been around for over 100 years; even if you don’t know it by that name, you’ve seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let’s call the missing tile ‘x’; the object of the puzzle is to arrange the tiles so that they are ordered as:
1 2 3 4

5 6 7 8

9 10 11 12

13 14 15 x

where the only legal operation is to exchange ‘x’ with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:
1 2 3 4        1 2 3 4        1 2 3 4        1 2 3 4

5 6 7 8        5 6 7 8        5 6 7 8        5 6 7 8

9 x 10 12        9 10 x 12        9 10 11 12        9 10 11 12

13 14 11 15        13 14 11 15        13 14 x 15        13 14 15 x

       r->           d->           r-> 

The letters in the previous row indicate which neighbor of the ‘x’ tile is swapped with the ‘x’ tile at each step; legal values are ‘r’,‘l’,‘u’ and ‘d’, for right, left, up, and down, respectively.

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing ‘x’ tile, of course).

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
arrangement.

Input

You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus ‘x’. For example, this puzzle
1 2 3

x 4 6

7 5 8

is described by this list:

1 2 3 x 4 6 7 5 8

Output

You will print to standard output either the word ``unsolvable’’, if the puzzle has no solution, or a string consisting entirely of the letters ‘r’, ‘l’, ‘u’ and ‘d’ that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.

Sample Input

2 3 4 1 5 x 7 6 8

Sample Output

ullddrurdllurdruldr

理解

	把输入的八数码转成12345678x要多少步,输出最短步数的走法(可能有多种)
	刚开始写的朴素的bfs,结构体里面保存路径,直接mle
	后来反向打了一个表,想着只有362880种可能性,但还是tle
	无奈看了大佬的a*算法,才a了这题

AC代码

#include <iostream>
#include <cmath>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;

int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
int vis[400000],path[400000];
int mvx[] = {0,0,1,-1};
int mvy[] = {1,-1,0,0};
int fsh[10][2] = {{2,2},{0,0},{0,1},{0,2},{1,0},{1,1},{1,2},{2,0},{2,1}};
char dir[] = {'r','l','d','u'};

struct node{
	int maps[10];
	int x,y;
	int step,ctr,mht;
	friend bool operator < (node a,node b){  ##  利用优先队列,把更接近答案的状态提前
		return a.step + a.mht > b.step + b.mht;
	}
} s; 

int cantor(int s[]){  ##  康拓展开,hash的思想
	int sum = 1;
	for(int i = 0;i < 9;i++){
		int cnt = 0;
		for(int j = i + 1;j < 9;j++){
			if(s[i] > s[j]) cnt++;
		}
		sum = cnt * fac[8 - i] + sum;
	}
	return sum;
}

int rep(int s[]){  ##  求反序列,完成剪枝
	int sum = 0;
	for(int i = 0;i < 9;i++){
		int cnt = 0;
		if(!s[i]) continue;
		for(int j = i + 1;j < 9;j++){
			if(s[i] > s[j] && s[j]) cnt++;
		}
		sum += cnt;
	}
	return sum;
}

int manhat(int s[]){  ##  用曼哈顿距离做完启发函数(不要考虑x的移动,其他到位了就是x也到位了,能省不少时间,之前考虑的时候63ms,不考虑只用32ms)
	int sum = 0;
	for(int i = 0;i < 9;i++){
		if(s[i]) sum += abs(fsh[s[i]][0] - i/3) + abs(fsh[s[i]][1] - i%3);
	}
	return sum;
}

void print(int rt){  ##  打印函数,路径下标为康拓展开的值,值为移动方向
	if(path[rt] == -1) return;
	print(path[rt]);
	cout << dir[vis[rt]];
}

int bfs(){
	priority_queue <node> q;
	q.push(s);
	
	node t,tt;
	while(!q.empty()){
		t = q.top();q.pop();
		for(int i = 0;i < 4;i++){
			tt = t;
			tt.x = t.x + mvx[i];tt.y = t.y + mvy[i];
			if(tt.x < 0 || tt.x > 2 || tt.y < 0 || tt.y > 2) continue;
			
			swap(tt.maps[t.x*3+t.y],tt.maps[tt.x*3+tt.y]);
			tt.ctr = cantor(tt.maps);
			tt.mht = manhat(tt.maps);
			
			if(vis[tt.ctr] == -1){
				vis[tt.ctr] = i;
				path[tt.ctr] = t.ctr;
				tt.step++;
				q.push(tt);
				if(!tt.mht){
					print(tt.ctr);
					cout << endl;
					return 1;
				}
			}
		}
	}
	return 0;
}

int main()
{
	char str[10];
	while(cin >> str[0]){
		for(int i = 1;i < 9;i++){
			cin >> str[i];
		}
		
		for(int i = 0;i < 9;i++){
			if(str[i] == 'x'){
				s.x = i / 3;
				s.y = i % 3;
				s.maps[i] = 0;
			}
			else s.maps[i] = str[i] - '0';
		}
		
		if((rep(s.maps) % 2)) cout << "unsolvable" << endl;  ##  用反序列把到不了的直接减掉,不进bfs浪费时间
		else{
			s.mht = manhat(s.maps);
			if(!s.mht) cout << endl;
			else{
				memset(vis,-1,sizeof(vis));
				memset(path,-1,sizeof(path));
				s.ctr = cantor(s.maps);
				s.step = 0;
				vis[s.ctr] = 1;
				bfs();
			}
		}
	}
	return 0;
}

K-Solitaire (dbfs,模拟) HDU - 1401

Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered from 1 to 8, from the top to the bottom and from left to right respectively.

There are four identical pieces on the board. In one move it is allowed to:

move a piece to an empty neighboring field (up, down, left or right),

jump over one neighboring piece to an empty field (up, down, left or right).

在这里插入图片描述

There are 4 moves allowed for each piece in the configuration shown above. As an example let’s consider a piece placed in the row 4, column 4. It can be moved one row up, two rows down, one column left or two columns right.

Write a program that:

reads two chessboard configurations from the standard input,

verifies whether the second one is reachable from the first one in at most 8 moves,

writes the result to the standard output.

Input

Each of two input lines contains 8 integers a1, a2, …, a8 separated by single spaces and describes one configuration of pieces on the chessboard. Integers a2j-1 and a2j (1 <= j <= 4) describe the position of one piece - the row number and the column number respectively. Process to the end of file.

Output

The output should contain one word for each test case - YES if a configuration described in the second input line is reachable from the configuration described in the first input line in at most 8 moves, or one word NO otherwise.

Sample Input

4 4 4 5 5 4 6 5
2 4 3 3 3 6 4 6

Sample Output

YES

理解

	大意就是按他的要求把棋子走到相应的位置即可(8步以内)
	书上说了可以用dbfs写(也没怎么说怎么从起点和结尾同时开始搜索)
	刚开始写的时候开了一个vis[8][8][8][8][8][8][8][8]标记状态,然后用1和2来记录是起点走的还是终
	点走的,且开了两个队列
	后面发现写出了  “灵异事件” ,起点第一个棋子走完第一步之后,终点第一个棋子会走第一步,然后回来
	继续起点棋子走一步。。。而且没有输出
	咋改都改不出来,后来看来大佬的写法,写成一个队列之后成功ac

AC代码

#include <iostream>
#include <cmath>
#include <cstdio>
#include <queue>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;

map <int,int> p[2];
int qp[9][9];
int mvx[] = {1,0,0,-1};
int mvy[] = {0,1,-1,0};

struct P {int x,y;};  ##  棋子
struct node{  ##  棋盘状态
	P point[4];
	int step,id;
} s,e;

bool comp(P a,P b){
	if(a.x == b.x) return a.y < b.y;
	return a.x < b.x;
}

int hashs(node k){
	sort(k.point,k.point + 4,comp);  ##  排序之后在hash,防止出现相同的情况
	int sum = 0;
	for(int i = 0;i < 4;i++){
		sum = sum*8 + k.point[i].x;
		sum = sum*8 + k.point[i].y;
	}
	return sum;
}

bool check(int x,int y){
	if(x < 1 || y < 1 || y > 8 || x > 8) return false;
	return true;
}

int bfs(){
    queue <node> q;
    q.push(s);
	q.push(e);
    
    while(!q.empty()){
        s = q.front();q.pop();
        if(p[!s.id][hashs(s)])return 1;
        
        memset(qp,0,sizeof(qp));
        for(int i=0;i<4;i++) qp[s.point[i].x][s.point[i].y]=1;
            
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                int tx=s.point[i].x+mvx[j],ty=s.point[i].y+mvy[j];
                if(check(tx,ty)){
                	if(qp[tx][ty]){  ##  如果去的位置有棋子,跳过它再判断
                	    tx += mvx[j];
                	    ty += mvy[j];
                	    if(!check(tx,ty) || qp[tx][ty]) continue;
                	}
                	
                	e = s;
               		e.step++;
                	e.point[i].x=tx;
                	e.point[i].y=ty;
                	
                	int hx=hashs(e);
                	if(p[!e.id][hx])return 1;
                	if(!p[e.id][hx]){
                		p[e.id][hx]=1;
                		if(e.step < 4) q.push(e);
                	}
                }
            }
        }
    }
    return 0;
}

int main()
{
	while(cin >> s.point[0].x >> s.point[0].y){
		for(int i = 1;i < 4;i++) cin >> s.point[i].x >> s.point[i].y;
		for(int i = 0;i < 4;i++) cin >> e.point[i].x >> e.point[i].y;
		s.step = 0;e.step = 0;
		s.id = 0;e.id = 1;
		p[s.id][hashs(s)] = 1;
		p[e.id][hashs(e)] = 1;
		
		p[0].clear();p[1].clear();
		if(bfs()) cout << "YES" << endl;
		else cout << "NO" << endl; 
	}
	return 0;
}

L-Eight II (ida*) HDU - 3567

Eight-puzzle, which is also called “Nine grids”, comes from an old game.

In this game, you are given a 3 by 3 board and 8 tiles. The tiles are numbered from 1 to 8 and each covers a grid. As you see, there is a blank grid which can be represented as an ‘X’. Tiles in grids having a common edge with the blank grid can be moved into that blank grid. This operation leads to an exchange of ‘X’ with one tile.

We use the symbol ‘r’ to represent exchanging ‘X’ with the tile on its right side, and ‘l’ for the left side, ‘u’ for the one above it, ‘d’ for the one below it.
在这里插入图片描述
A state of the board can be represented by a string S using the rule showed below.
在这里插入图片描述
The problem is to operate an operation list of ‘r’, ‘u’, ‘l’, ‘d’ to turn the state of the board from state A to state B. You are required to find the result which meets the following constrains:

  1. It is of minimum length among all possible solutions.
  2. It is the lexicographically smallest one of all solutions of minimum length.

Input

The first line is T (T <= 200), which means the number of test cases of this problem.

The input of each test case consists of two lines with state A occupying the first line and state B on the second line.
It is guaranteed that there is an available solution from state A to B.

Output

For each test case two lines are expected.

The first line is in the format of “Case x: d”, in which x is the case number counted from one, d is the minimum length of operation list you need to turn A to B.
S is the operation list meeting the constraints and it should be showed on the second line.

Sample Input

2
12X453786
12345678X
564178X23
7568X4123

Sample Output

Case 1: 2
dd
Case 2: 8
urrulldr

理解

	找出最小字典序的路径的串
	拿刚学的a*写了一遍,发现调整不了输出路径的字典序,瞬间自闭
	dbfs也不会写,想不通该咋做到字典序最小(起点从小到大,终点从大到小?)
	最后学了大佬一手ida*,成功a题

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;

int maps[10];
int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
int vis[400000];
char path[400000];
int mvx[] = {1,0,0,-1};
int mvy[] = {0,-1,1,0};
int fsh[10][2],nexts = 0x3f3f3f3f,cnts = 0;
char dir[] = {'d','l','r','u'};
 
int cantor(int s[]){  ##  hash
	int sum = 0;
	for(int i = 0;i < 9;i++){
		int cnt = 0;
		for(int j = i + 1;j < 9;j++){
			if(s[i] > s[j]) cnt++;
		}
		sum = cnt * fac[8 - i] + sum;
	}
	return sum;
}

bool check(int x,int y){
	if(x < 0 || x > 2 || y > 2 || y < 0) return 0;
	return 1;
}
 
int manhat(int s[]){  ##  启发函数
	int sum = 0;
	for(int i = 0;i < 9;i++){
		if(s[i]) sum += abs(fsh[s[i]][0] - i/3) + abs(fsh[s[i]][1] - i%3);
	}
	return sum;
}

bool ida(int k,int step,int want){
    int mht = manhat(maps);
    if(step + mht > want){
        nexts = min(nexts,step + mht);
        return 0;
    }
 
    if(!mht){
        path[step] = '\0';
        printf("Case %d: %d\n",cnts,step);
        puts(path);
        return 1;
    }
 
    int x = k / 3,y = k % 3;
    for(int i = 0; i<4; i++){
        int tx = x + mvx[i];
        int ty = y + mvy[i];
        if(check(tx,ty)){
            int tk = tx*3 + ty;
            swap(maps[k],maps[tk]);
            int hx = cantor(maps);
            if(!vis[hx]){
                vis[hx] = 1;
                path[step] = dir[i];
                if(ida(tk,step + 1,want)) return 1;
                vis[hx] = 0;
            }
            swap(maps[k],maps[tk]);
        }
    }
    return 0;
}
 
int main()
{
    int t,k;
    cin >> t;
    for(cnts = 1;cnts <= t;cnts++){
        string str;
		cin >> str;
		for(int i = 0;i < str.size();i++){
			if(str[i] == 'X'){
				maps[i] = 0;
				k = i;
			}
			else maps[i] = str[i] - '0';
		}
		
		cin >> str;
		for(int i = 0;i < str.size();i++){
			if(str[i] == 'X'){
				fsh[0][0] = i / 3;
				fsh[0][1] = i % 3;
			}
			else{
				fsh[str[i] - '0'][0] = i / 3;
				fsh[str[i] - '0'][1] = i % 3;
			}
		}
	
        for(int want = manhat(maps);;want = nexts){  ##  以刚开始的曼哈顿距离当期望数,达不到就更新期望
            nexts = 0x3f3f3f3f;
            memset(vis,0,sizeof(vis));
            vis[cantor(maps)] = 1;
            if(ida(k,0,want)) break;
        }
    }
    return 0;
}

M-Power Calculus (ida*) POJ - 3134

Starting with x and repeatedly multiplying by x, we can compute x31 with thirty multiplications:

x2 = x × x, x3 = x2 × x, x4 = x3 × x, …, x31 = x30 × x.

The operation of squaring can be appreciably shorten the sequence of multiplications. The following is a way to compute x31 with eight multiplications:

x2 = x × x, x3 = x2 × x, x6 = x3 × x3, x7 = x6 × x, x14 = x7 × x7, x15 = x14 × x, x30 = x15 × x15, x31 = x30 × x.

This is not the shortest sequence of multiplications to compute x31. There are many ways with only seven multiplications. The following is one of them:

x2 = x × x, x4 = x2 × x2, x8 = x4 × x4, x8 = x4 × x4, x10 = x8 × x2, x20 = x10 × x10, x30 = x20 × x10, x31 = x30 × x.

If division is also available, we can find a even shorter sequence of operations. It is possible to compute x31 with six operations (five multiplications and one division):

x2 = x × x, x4 = x2 × x2, x8 = x4 × x4, x16 = x8 × x8, x32 = x16 × x16, x31 = x32 ÷ x.

This is one of the most efficient ways to compute x31 if a division is as fast as a multiplication.

Your mission is to write a program to find the least number of operations to compute xn by multiplication and division starting with x for the given positive integer n. Products and quotients appearing in the sequence should be x to a positive integer’s power. In others words, x−3, for example, should never appear.

Input

The input is a sequence of one or more lines each containing a single integer n. n is positive and less than or equal to 1000. The end of the input is indicated by a zero.

Output

Your program should print the least total number of multiplications and divisions required to compute xn starting with x for the integer n. The numbers should be written each in a separate line without any superfluous characters such as leading or trailing spaces.

Sample Input

1
31
70
91
473
512
811
953
0

Sample Output

0
6
8
9
11
9
13
12

理解

	书上也讲的很明白了,当成n的加减法,自己判断深度够不够,不够就继续加

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;

int n,k;
int ss[1010];

bool ida(int now,int want){
    if(ss[k] < 0) return 0;  ##  不存在负数的可能性
	if(now > want) return 0;  ##  当前深度以大于预期值
    if(ss[k] << (want - now) < n) return 0;  ##  2呈指数倍增加还是不够,深度就是不够
    if(ss[k] == n) return 1;
    
    k++;
    for(int i = 0;i < k;i++){
    	ss[k] = ss[k - 1] + ss[i];  ##  尝试和之前所有的数相加的结果
    	if(ida(now + 1,want)) return 1;
    	ss[k] = ss[k - 1] - ss[i];  ##  同理,尝试减法
    	if(ida(now + 1,want)) return 1; 
	}
	k--;
	return 0;
}
 
int main()
{
    while(cin >> n && n){
    	int i;
    	for(i = 0;;i++){
    		ss[0] = 1;
    		k = 0;
    		if(ida(0,i)) break;
		}
		cout << i << endl;
	}
    return 0;
}

N-DNA sequence (ida*)HDU - 1560

The twenty-first century is a biology-technology developing century. We know that a gene is made of DNA. The nucleotide bases from which DNA is built are A(adenine), C(cytosine), G(guanine), and T(thymine). Finding the longest common subsequence between DNA/Protein sequences is one of the basic problems in modern computational molecular biology. But this problem is a little different. Given several DNA sequences, you are asked to make a shortest sequence from them so that each of the given sequence is the subsequence of it.

For example, given “ACGT”,“ATGC”,“CGTT” and “CAGT”, you can make a sequence in the following way. It is the shortest but may be not the only one.
在这里插入图片描述

Input

The first line is the test case number t. Then t test cases follow. In each case, the first line is an integer n ( 1<=n<=8 ) represents number of the DNA sequences. The following k lines contain the k sequences, one per line. Assuming that the length of any sequence is between 1 and 5.

Output

For each test case, print a line containing the length of the shortest sequence that can be made from these sequences.

Sample Input

1
4
ACGT
ATGC
CGTT
CAGT

Sample Output

8

理解

	大意也是比较简单,自己创造一个最短的串,能把给的串通过相同相消的方式消除干净就行
	每次dfs枚举4^n个串就好,n为深度

AC代码

#include <iostream>
#include <queue>
#include <cstring>
#include <map>
#include <vector>
using namespace std;

int n,len;
string s[10],jy = "ACGT";
int lf[10];

int lfs(){  ##  启发函数,计算还差最长还有几个字母没被消掉
	int maxs = 0;
	for(int i = 0;i < n;i++){
		if(s[i].size() - lf[i] > maxs) maxs = s[i].size() - lf[i];
	}
	return maxs;
}

int dfs(int now){
	if(now + lfs() > len) return 0;
	if(now == len) return 1;
	
	int temp[10];
	for(int i = 0;i < 4;i++){
		memcpy(temp,lf,sizeof(temp));
		int f = 0;
		for(int j = 0;j < n;j++){
			if(s[j][lf[j]] == jy[i]){
				f = 1;
				lf[j]++;
			}
		}
		if(f){  ## 剪枝,如果这字母啥都没消掉,就不考虑继续枚举这个位置为这个字母的串了
			if(dfs(now + 1)) return 1;
			memcpy(lf,temp,sizeof(lf));
		}
	}
	return 0;
}

int main()
{
	int t;
	cin >> t;
	while(t--){
		memset(lf,0,sizeof(lf));
		len = 0;
		
		cin >> n;
		for(int i = 0;i < n;i++){
			cin >> s[i];
			if(s[i].size() > len) len = s[i].size();
		}
		
		for(;;len++){if(dfs(0))break;}
		cout << len << endl;
	}
	return 0;
}

O-The Rotation Game (ida*,模拟) HDU - 1667

The rotation game uses a # shaped board, which can hold 24 pieces of square blocks (see Fig.1). The blocks are marked with symbols 1, 2 and 3, with exactly 8 pieces of each kind.
在这里插入图片描述

Initially, the blocks are placed on the board randomly. Your task is to move the blocks so that the eight blocks placed in the center square have the same symbol marked. There is only one type of valid move, which is to rotate one of the four lines, each consisting of seven blocks. That is, six blocks in the line are moved towards the head by one block and the head block is moved to the end of the line. The eight possible moves are marked with capital letters A to H. Figure 1 illustrates two consecutive moves, move A and move C from some initial configuration.

Input

The input consists of no more than 30 test cases. Each test case has only one line that contains 24 numbers, which are the symbols of the blocks in the initial configuration. The rows of blocks are listed from top to bottom. For each row the blocks are listed from left to right. The numbers are separated by spaces. For example, the first test case in the sample input corresponds to the initial configuration in Fig.1. There are no blank lines between cases. There is a line containing a single `0’ after the last test case that ends the input.

Output

For each test case, you must output two lines. The first line contains all the moves needed to reach the final configuration. Each move is a letter, ranging from A' toH’, and there should not be any spaces between the letters in the line. If no moves are needed, output `No moves needed’ instead. In the second line, you must output the symbol of the blocks in the center square after these moves. If there are several possible solutions, you must output the one that uses the least number of moves. If there is still more than one possible solution, you must output the solution that is smallest in dictionary order for the letters of the moves. There is no need to output blank lines between cases.

Sample Input

1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0

Sample Output

AC
2
DDHH
2

理解

	题意理解起来不难,通过八种操作,使中间的八个数字相等即可
	用ida*很简单就可以a掉

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;

int maps[8][8];
char dir[] = "ABCDEFGH";
char path[1000];
int mv[] = {3,5,3,5,5,3,5,3};

int manhat(){  启发函数,找出离目标最小还差几个数字
	int sum[4] = {0};
	for(int i = 3;i <= 5;i++){
		for(int j = 3;j <= 5;j++){
			sum[maps[i][j]]++;
		}
	}
	return 8 - max(sum[1],max(sum[2],sum[3]));
}

void move1(int x){  ##  移动操作(模拟)
	if(x == 0 || x == 1){
		int k = maps[1][mv[x]];
		for(int i = 1;i <= 6;i++){
			maps[i][mv[x]] = maps[i + 1][mv[x]];
		}
		maps[7][mv[x]] = k;
	}
	else if(x == 2 || x == 3){
		int k = maps[mv[x]][7];
		for(int i = 7;i >= 2;i--){
			maps[mv[x]][i] = maps[mv[x]][i - 1];
		}
		maps[mv[x]][1] = k;
	}
	else if(x == 4 || x == 5){
		int k = maps[7][mv[x]];
		for(int i = 7;i >= 2;i--){
			maps[i][mv[x]] = maps[i - 1][mv[x]];
		}
		maps[1][mv[x]] = k;
	}
	else if(x == 6 || x == 7){
		int k = maps[mv[x]][1];
		for(int i = 1;i <= 6;i++){
			maps[mv[x]][i] = maps[mv[x]][i + 1];
		}
		maps[mv[x]][7] = k;
	}
	return;
}

void remove1(int x){  ##  移回来(回溯)
	if(x == 0 || x == 1){
		int k = maps[7][mv[x]];
		for(int i = 7;i >= 2;i--){
			maps[i][mv[x]] = maps[i - 1][mv[x]];
		}
		maps[1][mv[x]] = k;
	}
	else if(x == 2 || x == 3){
		int k = maps[mv[x]][1];
		for(int i = 1;i <= 6;i++){
			maps[mv[x]][i] = maps[mv[x]][i + 1];
		}
		maps[mv[x]][7] = k;
	}
	else if(x == 4 || x == 5){
		int k = maps[1][mv[x]];
		for(int i = 1;i <= 6;i++){
			maps[i][mv[x]] = maps[i + 1][mv[x]];
		}
		maps[7][mv[x]] = k;
	}
	else if(x == 6 || x == 7){
		int k = maps[mv[x]][7];
		for(int i = 7;i >= 2;i--){
			maps[mv[x]][i] = maps[mv[x]][i - 1];
		}
		maps[mv[x]][1] = k;
	}
	return;
} 

bool dfs(int step,int want){
	int k = manhat();
	if(step + k > want) return 0;
	if(step == want){
		path[step] = '\0';
		puts(path);
		cout << maps[3][3] << endl;
		return 1;
	}
	
	for(int i = 0;i < 8;i++){
		move1(i);
		path[step] = dir[i];
		if(dfs(step + 1,want)) return 1;
		remove1(i);
	}
	return 0;
}
 
int main()
{
	int x;
    while(cin >> x && x){
    	memset(maps,0,sizeof(maps));
    	maps[1][3] = x;
    	cin >> maps[1][5];
    	for(int i = 2;i <= 7;i++){
    		if(i == 3 || i == 5){
    			for(int j = 1;j <= 7;j++){
    				cin >> maps[i][j];
				}
			}
			else{
				cin >> maps[i][3] >> maps[i][5];
			}
		}
		
//		for(int i = 1;i <= 7;i++){
//			for(int j = 1;j <= 7;j++){
//				cout << maps[i][j] << ' ';
//			}
//			cout << endl;
//		}
//		cout << endl;
//		move1(7);remove1(7);
//		for(int i = 1;i <= 7;i++){
//			for(int j = 1;j <= 7;j++){
//				cout << maps[i][j] << ' ';
//			}
//			cout << endl;
//		}
		
		int k = manhat();
		if(k){
			for(int i = 1;;i++){
				if(dfs(0,i)) break;
			}
		}
		else{
			cout << "No moves needed" << endl;
			cout << maps[3][3] << endl;
		}
	}
	return 0;
}

(三)一些收获

关于dfs

dfs的学习可能是因为是一些比较基础的内容,所以压力还没有那么大(后面那些魔鬼剪枝当我没说),实际上目前看到的大部分dfs还是和回溯结合进行操作的,在一些特殊情况下会不用回溯,下面总结了一些可能用到dfs的题型

	1.判断这条路是否能走通(触底反弹到找到一条即可)
	2.输出最小字典序的答案或者按字典序输出所有能找到的答案
	3.判断有多少种可能性
	4.找出最小值或者最大值

题目要求的东西可能是多重要求的总和,重点还是得看题目要求,选择适当的方法进行处理

关于bfs

如果说dfs是类似于树搜到低然后再返回,bfs就是树一层层节点遍历过去,用铺天盖地的方式找出结果,但因为不用一次次搜到底再返回,在一些题目上有显著的优势,一些可能用到bfs的题型

	1.判断这条路是否能走通(触底反弹到找到一条即可)
	2.找出最小值或者最大值(特殊情况可以用,具体看题目)
	3.最短步数或者最小拐弯数

关于dbfs

对dbfs说实话感触不深,毕竟只写了一题 “Solitaire” ,但大概还是知道怎么写了,根据bfs大面积铺地毯式的操作方式,dbfs确实是减少了铺地毯的面积,以后希望还可以遇到类似题目再练练(其实 “Eight II ”)完全可以拿来练dbfs来着,就是真的太忙了,之前的题都没空补。。。

关于a*算法

唯一写的几题,都是套了曼哈顿距离的启发函数,感触还不是很深,但大概理解了他的运作方式,我们可以用走迷宫的方式理解bfs,先来后到的公平原则使他能按步数均匀地铺地毯

而加上a的bfs则改变了铺地毯的方式,他会选择最能接近目标的地方有限铺地毯,实际上用a算法的bfs就是自己定义了一个优先级,改变原先得公平原则(可以用优先队列实现)

启发函数是灵魂,好的启发函数才能完成优秀的bfs “剪枝”

关于ida*

ida*实际上就是在无法预测深度的情况下,自行设立深度,以防止搜索深度过深tle,或者广度过广mle,虽然会重复小规模dfs,但比起不知深度的无限搜索优化了特别多

关键也是启发函数,用来确定是不是已经到不了终点了,及时停止dfs以继续扩大预期值

(四)感想

这个星期也算是在dfs和bfs上面入了一个门,对于简单的dfs和bfs题目已经能做做看了,需要剪枝的也能做出尝试,感觉一些过去学的东西貌似在这一周也给了我很多帮助

1.上周写的模拟确实是对有了一个不错的帮助,因为脑子可以模拟比较简单的dfs和bfs搜索过程了,对于两者的理解也是更深了,而写一些模拟类操作时,感觉压力也已经小了很多

2.曾经看的链表(虽然只看了单向链表),确实是对理解dfs是有帮助的,而且帮助还不小,至少你在知道树这个结构和栈的前提下,理解dfs就是另一回事了,和当初刚学递归时一脸懵逼的状况相比好了很多,至少有基础垫一下了

3.开始能在递归里面找错,具体方式就是锁定区域,重新思考(看中间量确实十分有用)

当然还是有非常多的不做

1.自己写的时候,一些难一点的剪枝还是构架不起来,对这一块的能力还是比较弱

2.对于更进一步的a* ida* dbfs写的题目还是太少了,只是懂了,但运用方面问题还是很大,在看完别人题解之后虽然能写大体,但细节上操作起来还是比较差

总结来说,这一周学的勉强还算可以,至少对dfs和bfs的入门是学了不少,关于后面更进一步的搜索算法之类的,之后再继续努力吧,这个暑假还是得兼顾广度

(革命尚未成功,同志尚需努力[狗头])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值