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;
}