DFS与排列组合(C语言描述)#2

题目描述:
输出自然数 1 到 n 所有不重复的排列,即 n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
样例输入:
3
样例输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
先上代码:
C语言描述带回溯DFS

#include<stdio.h>
int n=0;
int k=0,l=0,m=0;
int a[100];
int b[100];
void dfs(int k)
{
    
    int i=0;
    if(k==n){
    
    
        for(i=0;i<n;i++)
        printf("%5d",a[i]);
        printf("\n");
        return;
    }
        for(i=1;i<=n;i++)
        if(b[i]==0)//这个数没用过
        {
    
    
        a[k]=i;//纳入数组
         b[i]=1;//标记使用
        dfs(k+1);//往下填
        b[i]=0;//回溯
        }
}
int main()
{
    
    scanf("%d",&n);
dfs(0);
    return 0;
}

此处注意两个点
1.dfs(k+1)不是k++,++k;
2.i别开全局,会在递归中数值被冲刷。
原理:
在这里插入图片描述
如图所示,遇到曾使用过元素进行剪枝,以【123】中以1开头为例
循环到1,进入递归 搜索 123,保留12 13(排除11) 继续搜索 保留 123 132排除(121,122,131,133)。
以上为排列。
补充:回溯递归DFS用于背包问题。
时间限制决定了DFS能否用于用于背包问题,从追求答案来说是可行的,但从时间角度考虑很难AC。
一维背包:

#include<stdio.h>
int n=0,m=0,x=0;
int a[103][3],b[15000];
int i=0,j=0,k=0,sum=0,l=0,p=0,y=0;
int c[101];
int max(int a,int b)
{
    
    
    if(a>=b)
        return a;
    else
        return b;
}
void dfs(int sum,int k,int i)
{
    
    
int z=0;
        int j=0;
for(j=i;j<=n;++j){
    
    
    if(c[j]==0)
              if(a[j][1]<=k)
        {
    
    z=1;
        c[j]=1;
        dfs(sum+a[j][0],k-a[j][1],i+1);
        c[j]=0;
        }
}
if(z==0)
    {
    
    y=max(y,sum);
    return;}
}
int main()
{
    
    int max=0;
scanf("%d %d",&m,&n);//m为重量限制,n为可选择数目
for(i=1;i<=n;i++)
{
    
    
    scanf("%d %d",&a[i][1],&a[i][0]);
}
dfs(0,m,1);
printf("%d",y);
return 0;
}

二维背包:

#include<stdio.h>
int n=0,m=0,x=0;
int a[103][3],b[15000];
int i=0,j=0,k=0,sum=0,l=0,p=0,y=0;
int c[101];
int max(int a,int b)
{
    
    
    if(a>=b)
        return a;
    else
        return b;
}
void dfs(int sum,int k,int l,int i)
{
    
    
int z=0;
        int j=0;

for(j=i;j<=n;++j){
    
    
    if(c[j]==0)
              if(a[j][1]<=k&&a[j][2]<=l)
        {
    
    z=1;
        c[j]=1;
        dfs(sum+a[j][0],k-a[j][1],l-a[j][2],i+1);
        c[j]=0;
        }
}
if(z==0)
    {
    
    y=max(y,sum);
    return;}
}
int main()
{
    
    int max=0;
scanf("%d %d %d",&n,&m,&x);//n为可选择数目,m为数据限制1,x为数据限制2
for(i=1;i<=n;i++)
{
    
    
    scanf("%d %d %d",&a[i][0],&a[i][1],&a[i][2]);
}
dfs(0,m,x,1);
printf("%d",y);
return 0;
}

两个代码都test了一圈,在数据够大时都会TL,但本身结果没问题。
上一篇的选择ornot树也一样可以用于滑雪问题
类似拿
dfs(i+1,j-1)
dfs(i+1,j+1)
递归,但同样,时间有限制,所以很多可以DFS的问题最后只能DP。
没办法,附上一维背包标答:

#include<stdio.h>
int dp[101][1005];
int max(int a,int b)
{
    
    
    if(a>=b)
        return a;
    else
        return b;
}
int main()
{
    
    int n=0,k=0,i=0,j=0,m=0;
int a[103],b[103];
scanf("%d %d",&n,&m);
for(i=1;i<=m;i++)
    scanf("%d%d",&a[i],&b[i]);
for(i=1;i<=m;i++)
   for(j=n;j>=0;j--)
   {
    
    
       if(a[i]<=j)
        dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+b[i]);
        else
        dp[i][j]=dp[i-1][j];
   }
   printf("%d",dp[m][n]);
    return 0;
}

总结:该DP就DP,哪怕DFS可以做,但不管代码还是时间复杂度DP看起来都更优秀。
虽然本来讲的是排列与组合;

原子在数量上是无限的,在形式上是多样的。
万物皆排列组合。
————德谟克利特

猜你喜欢

转载自blog.csdn.net/weixin_43736127/article/details/105949271