DFS问题

7-1 整数分解为若干项之和 (20分)

输入格式:
每个输入包含一个测试用例,即正整数N (0<N≤30)。

输出格式:
按递增顺序输出N的所有整数分解式子。递增顺序是指:对于两个分解序列N
​1
​​ ={n
​1
​​ ,n
​2
​​ ,⋯}和N
​2
​​ ={m
​1
​​ ,m
​2
​​ ,⋯},若存在i使得n
​1
​​ =m
​1
​​ ,⋯,n
​i
​​ =m
​i
​​ ,但是n
​i+1
​​ <m
​i+1
​​ ,则N
​1
​​ 序列必定在N
​2
​​ 序列之前输出。每个式子由小到大相加,式子间用分号隔开,且每输出4个式子后换行。

输入样例:
7

输出样例:
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;7=7

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

int n=0;
int m;
int sum=0;
int s[31];
int t=0;

void dfs(int k){
    if(sum==m){
        n++;
        cout<<m<<"="<<s[0];
        for(int i=1;i<t;i++){
            cout<<"+"<<s[i];
        }
        if(n%4==0||t==1){
            cout<<endl;
        }
        else cout<<";";
    }
    if(sum>m)return ;
    for(int i=k;i<=m;i++){
        sum+=i;//求和,判断是否该结束,如果大于就直接return
        s[t]=i;
        t++;
       dfs(i);//他会从当前的确定值的后面开始dfs,比如确定前两个为1 2,那么接着会dfs(2),确保后面的大于等于2,所以只会出现4=1+1+2而不是4=1+2+1

        sum-=i;//继续搜索,恢复上个状态
        t--;
    }

}



int main(){

    cin>>m;
    dfs(1);
    return 0;


}

dfs的全排列
摘自 https://blog.csdn.net/weixin_43272781/article/details/82959089(深度优先搜索算法)

#include<bits/stdc++.h>
using namespace std;
int n;
char a[15];
int r[15];
int v[15];
void dfs(int step)
{
    if(step==n+1)
    {
        for(int i=1; i<=n; i++)
        {
            printf("%c",r[i]);
        }
        cout<<endl;
        return ;
    }
    else
    {
        for(int i=1; i<=n; i++)
        {
            if(v[i]==0)
            {
                r[step]=a[i];
                v[i]=1;
                dfs(step+1);
                v[i]=0;
            }

        }
        return ;
    }

}

int main()
{
    memset(v,0,sizeof(v));
    scanf("%s",a+1);
    n = strlen(a+1);//字符串的长度
    dfs(1);//step从1开始


}

HDU 2955

#include<bits/stdc++.h>
using namespace std;
double max(double a ,double b){
    if(a>b)return a;
    else return b;

}
double dp[10006];
struct Bank
{
    int money;//钱数
    double gailv;//别抓的概率
    double tp;//成功逃走的概率

}bank[102];
int main(){
    int T;
    cin>>T;
    while(T--){
        double z;//最小安全值,小于这个数就不行
        int N;//The number of banks
        int maxmoney = 0;
        cin>>z>>N;
        for(int i=1;i<=N;i++){//输入
            cin>>bank[i].money>>bank[i].gailv  ;//钱数和被抓的概率
            bank[i].tp=1.0-bank[i].gailv;
            maxmoney += bank[i].money;
        }
        memset(dp,0,sizeof(dp));
        dp[0]=1.0;//只有dp[0]=1,当遇到dp[j-bank[i].money==0]才可以计算出dp的值
        for(int i=1;i<=N;i++){
            for(int j=maxmoney;j>=bank[i].money;j--){//从最大开始计算,这样就可以算出比已知bp大的的值,比如maxmoney=7=2+5,那bp[5]=bp[7-2]
                dp[j]=max(dp[j],dp[j-bank[i].money]*bank[i].tp);//计算的是可以逃走的值,通过概率论知识可以知道总的是每个事件逃走概率的乘积。
            }
        }
        for(int i=maxmoney;i>=0;i--){
            if(1-dp[i]<=z){
                cout<<i<<endl;
                break;
            }
        }









    }









}

HDU 1312
  • 开始还觉得是个迷宫题,后来觉得差不多,这个就是找所有能走的空格,也直接用DFS就行,进行上下左右变换。
#include<bits/stdc++.h>
using namespace std;
int m,n;
int sum;
char a[25][25];
int f[4][2]={{-1,0},{1,0},{0,1},{0,-1}};
void dfs(int h,int l)
{
    sum++;
    a[h][l]='#';//走过之后就另其为#,之后不能再走
    int hh,ll;//上下左右进行变换
    for(int i=0;i<4;i++){
            hh=h+f[i][0];
            ll=l+f[i][1];
            if(hh>0&&ll>0&&hh<=n&&ll<=m&&a[hh][ll]=='.'){
                dfs(hh,ll);
            }


    }

}
int main()
{


    while(cin>>m>>n)//m是列,n是行
    {
        //getchar();
        if(n==0&&m==0)
            return 0;

        memset(a,0,sizeof(a));//先全归0
        int h,l;//确定@的位置
        for(int i=1; i<=n; i++)//hang
        {
            for(int j=1; j<=m; j++)//lie
            {
                cin>>a[i][j];
                if(a[i][j]=='@')
                {
                    h=i;//行和列
                    l=j;
                }
            }
            getchar();//换行

        }//输入结束
        sum =0 ;
        dfs(h,l);
        cout<<sum<<endl;





    }

}

  • Java版
public class oj{
	private static int d[][]= {{-1,0},{1,0},{0,1},{0,-1}};
	static int m,n;
	static int sum;
	static char a[][] = new char [25][25];
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		while(in.hasNext()) {
			m = in.nextInt();
			n= in.nextInt();
			if(n==0&&m==0)return ;
			
			int h=0,l = 0;
			for(int i=0;i<n;i++) {
				String s = in.next();//java不能直接读取单个字符,所以用字符串过渡一下
				for(int j=0;j<m;j++) {   //这时如果还是i=1j=1来输入,会造成charAt()函数报错,英文当j=m时,s[j]则没有保存字符,所以那个dfs的范围也要相应改变
				
					a[i][j]= s.charAt(j);
					if(a[i][j]=='@') {
						h=i;
						l=j;
					}
				}
			}
			sum=0;
			dfs(a,h,l);
			System.out.println(sum);
			
		}
		
	}
	private static void dfs(char[][] a,int h,int l) {
		sum++;
		a[h][l]='#';//标记已经经过
		int hh=h,ll=l;
		for(int i=0;i<4;i++) {
			hh=h+d[i][0];
			ll=l+d[i][1];
			if(hh>=0&&ll>=0&&hh<n&&ll<m&&a[hh][ll]=='.') {
				dfs(a, hh, ll);
			}
		}
	}
	
	
}


HDU 1214
  • 和上个题差不多,上个是找出来能到步数,上下左右dfs,这次只是多了个斜着的,
#include<bits/stdc++.h>
using namespace std;
char a[101][101];
int sum;
int m,n;//m是行,n是列
void dfs(int h,int l){
    a[h][l]='*';
    int hh,ll;
    for(int i=-1;i<2;i++){
        for(int j=-1;j<2;j++){
            hh=h+i;
            ll=l+j;
            if(hh>0&&ll>0&&hh<=m&&ll<=n&&a[hh][ll]=='@'){
                dfs(hh,ll);
            }
        }
    }


}


int main()
{

    while(cin>>m>>n)
    {
        sum=0;//每次开始自定义为0
        if(m==0)
            return 0;
        for(int i=1; i<=m; i++)
        {
            for(int j=1; j<=n; j++)
            {
                cin>>a[i][j];

            }
        }//输入结束,遇到一个块就把他变为*,然后继续找@
        for(int i=1; i<=m; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(a[i][j]=='@')
                {
                    dfs(i,j);//
                    sum++;
                }

            }
        }
        cout<<sum<<endl;

    }

}

HDU1010

只是在dfs中判断搜索是否完成,不一定遍历所有的可走的的块,还有一个就是需要dfs一次后将原来改变的区块再变回来继续寻找可以成功的路。

#include<bits/stdc++.h>
using namespace std;
int m,n,t;
char a[9][9];
int b[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};
int bg1,bg2;//开始时的坐标
int d1,d2;//门的坐标
int flag,wall;
void dfs(int x,int y,int s)
{
    int p,x1,y1;
    if(x<=0||x>n||y<=0||y>m)//越界
        return ;
    if(x==d1&&y==d2&&s==t)
    {
        flag=1;
        return;//按时到达门口的位置
    }
    p=(t-s)-abs(x-d1)-abs(y-d2);//t-s是目前还剩的步数,肯定不会比当前位置到D的最短距离还近
    if(p<0||p%2!=0) //p是奇数
    {
        return ;
    }
    for(int j=0; j<4; j++)
    {
        x1=x+b[j][0];
        y1=y+b[j][1];
        if(a[x1][y1]!='X')
        {
            a[x1][y1]='X';
            dfs(x1,y1,s+1);
            if(flag)
                return ;
            a[x1][y1]='.';//如果不成功返回后还要复原继续寻找
        }
    }


    return;

}
int main()
{
    while(cin>>n>>m>>t)
    {
        wall=0;
        flag=0;
        if(m==n&&n==t&&t==0)
            return 0;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                cin>>a[i][j];
                if(a[i][j]=='S')
                {
                    bg1=i;
                    bg2=j;

                }
                if(a[i][j]=='D')
                {
                    d1=i;
                    d2=j;
                }
                if(a[i][j]=='X')
                    wall++;//墙的数目

            }
        }
        if(n*m-wall<=t)
        {
            printf("NO\n");
            continue;
        }
        a[bg1][bg2]='X';//将起点重定义为不可通过
        dfs(bg1,bg2,0);
        if(flag)
            printf("YES\n");
        else
            printf("NO\n");






    }
}

HDU1016 Prime Ring Problem
#include<bits/stdc++.h>
using namespace std;
int a[21];
int v[21];//之前是否走过,标记位
int prime[40]={0,1,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0};
int n;

void dfs(int m)//用这个dfs遍历每一种可能,有点像优化的排列组合
{
    if(m==n&&prime[a[m-1]+a[0]])//符合条件输出
    {
        for(int x=0;x<n-1;x++){
            cout<<a[x]<<" ";
        }
        cout<<a[n-1]<<endl;

    }
    else {

        for(int t=2;t<=n;t++){
            if(v[t]){
                if(prime[t+a[m-1]]){
                    v[t]=0;
                    a[m]=t;
                    m++;
                    dfs(m);
                    v[t]=1;//取消标记再次进行查找
                    m--;
                }
            }
        }

    }

}

int main()
{

    int i=0;
    while(cin>>n)
    {
        i++;
        cout<<"Case "<<i<<":"<<endl;//先输出这句,后面符合条件就直接输出了
        memset(v,1,sizeof(v));
        a[0]=1;//固定
        dfs(1);

        cout<<endl;

    }
}

HDU1072
#include<bits/stdc++.h>
using namespace std;
int n,m;//行列
int a[20][20];//房间排列
int step[20][20];//步数
int t[20][20];//时间
int ti;
int b[4][2]={{-1,0},{1,0},{0,1},{0,-1}};
void dfs(int bn,int bm,int s1,int s2)
{
    if(s1<=0||s2>=64)return ;//时间和步数超出范围
    if(bn<=0||bn>n||bm>m||bm<=0)return ;
    if(a[bn][bm]==0)return ;//墙
    if(a[bn][bm]==3)
    {
        ti=min(ti,s2);
        return ;//较短的时间
    }
    if(a[bn][bm]==4)
    {
        s1=6;
    }
    if(step[bn][bm]<=s2&&t[bn][bm]>=s1){//这个更差,剪枝
        return ;
    }
    step[bn][bm]=s2;
    t[bn][bm]=s1;
    for(int i=0;i<4;i++)
    {
        int nn=bn+b[i][0];
        int mm=bm+b[i][1];
        dfs(nn,mm,s1-1,s2+1);
    }


}
int main()
{
    int N;
    int bn,bm,en,em;//开始和结束的坐标
    cin>>N;
    for(int i=0;i<N;i++){
        scanf("%d %d",&n,&m);//行列
        for(int x=1;x<=n;x++){
            for(int y=1;y<=m;y++){
                scanf("%d",&a[x][y]);
                t[x][y]=0;
                step[x][y]=64;
                if(a[x][y]==2){
                    bn=x;
                    bm=y;
                }
                if(a[x][y]==3){
                    en=x;
                    em=y;
                }

            }

        }//
        ti=64;
        dfs(bn,bm,6,0);
        if(ti==64)cout<<"-1"<<endl;
        else printf("%d\n",ti);




    }

}

HDU 1045
#include<bits/stdc++.h>
using namespace std;
int n;
int sum;
char maze[5][5];
bool ok(int i,int j)
{
    if(maze[i][j]=='X'||maze[i][j]=='B')
        return 1;
    //判断这行这列是否有堡垒
    for(int x=i; x>=1; x--)//这里要注意,不能直接从1到n,因为如果有X相隔也是符合要求,但是这个会忽略这种情况。比如BX. 这里的.从 1 开始的话就直接遍历到B结束。
    {
        if(maze[x][j]=='B')
            return 1;
        if(maze[x][j]=='X')
            break;//
    }
    for(int x=i; x<=n; x++)
    {
        if(maze[x][j]=='B')
            return 1;
        if(maze[x][j]=='X')
            break;
    }
    for(int x=j; x>=1; x--)
    {
        if(maze[i][x]=='B')
            return 1;
        if(maze[i][x]=='X')
            break;//
    }
    for(int x=j; x<=n; x++)
    {
        if(maze[i][x]=='B')
            return 1;
        if(maze[i][x]=='X')
            break;
    }
    //都没问题
    return 0;


}
void dfs(int a,int b,int cn)//假设所以的.都可以放堡垒,这里
{
    if(cn>sum)
        sum=cn;
        int flag=0;
    for(int i=a; i<=n; i++)
    {

        for(int j=1; j<=n; j++)
        {
            if(i==a&&j<=b)
                continue;
            if(ok(i,j))//这个点不适合放
                continue;
            flag=1;
            maze[i][j]='B';
            dfs(i,j,cn+1);
            maze[i][j]='.';//取消标志,继续寻找,
        }

    }
    if(flag==0)return;//如果不再进行dfs就return返回上一个。




}
int main()
{
    while(cin>>n)
    {
        if(n==0)
            break;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                cin>>maze[i][j];
            }

        }

        sum=0;
        dfs(1,0,0);
        cout<<sum<<endl;
    }
    return 0;


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值