Yao Lirong's Blog

P1118 Backward Digital Sums

2019/07/02

题目来源:洛谷P1118 数字三角形


暴力搜索

看到题目的第一眼想的就是直接搜,发现不少TLE,我还减了不少枝…

超时,只有20分,通过几个运行结果来看,写法应该对了,只是全都TLE而已

  1. ^在c++中不是幂运算,是异或(如果两个相应位为“异”(值不同),则该位结果为1,否则为0。0^0=0; 0^1=1; 1^0=1; 1^1=0)。幂运算用pow(base,power)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,tri[13][13],ans[13]; bool flag=true;
void dfs(int);
int main()
{
int start;
cin>>n>>start; tri[n][1]=start;

dfs(n-1);

if(!flag) for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
return 0;
}
void dfs(int level)
{
if(flag){
int lbound=pow(2,level-1),ubound=tri[level+1][1]-lbound;///lbound(lower_bound)是假设第一行全是1,得出的后续几行的最小值
for(int i=lbound;i<=ubound;i++){
if(!flag) continue;
tri[level][1]=i;
bool jump=false;
for(int j=1;j<=n-level;j++){
tri[level][j+1]=tri[level+1][j]-tri[level][j];
///可以加一个判定条件,如果小于XXX,就停止本次搜索
if(tri[level][j+1]<lbound) {jump=true; break;}///与前面的lbound定义一样,如果一不下心搜出来0或者-1那么肯定不对
if(tri[level][j+1]==tri[level][j]) {jump=true; break;}///自己发现的规律,相邻两项不可能相等,因为相邻的两项相等会导致再上面一层也有两项相等,直到第一层有两项相等,意味着答案不符合条件
}
if(jump) continue;

for(int j=1;j<=n-level+1;j++){
cout<<tri[level][j]<<" ";
}
cout<<endl;

if(level==1){
int sum=0;
for(int i=1;i<=n;i++){
sum+=pow(2,tri[1][i]);
sum-=pow(2,i);
}///这个地方用了一个二进制的性质算我们的答案第一行是否是1~N的数字且互不相同(每个1~N的数都有两种状态,在答案里有或者没有)
if(sum==0){///sum==0 代表sum(pow(2,tri[1][i]))==sum(pow(2,i))
for(int i=1;i<=n;i++) ans[i]=tri[1][i];
flag=false;
} /// 停止所有搜索,已找到答案
}

if(level>1&&flag==true) dfs(level-1);
}
}
}

杨辉三角

以下为杨辉三角一维dfs,只有70分,还是需要剪枝

首先要搞清楚这个数字三角形究竟是什么吧。大家可以自己在草稿纸上写一下,假设n为一个比较小的数(比如,按样例,4),设第一行的n个数分别为a,b,c,…(我这里是a,b,c,d),然后模拟加一下,就会发现sum是……

如果n为4,那么sum是a+3b+3c+d。

如果n为5,那么sum是a+4b+6c+4d+e。

如果n为6,那么sum是a+5b+10c+10d+5e+f。

观察各项的系数,你发现了什么?


如果你有敏锐的数学眼光,你会发现,各项系数恰与杨辉三角有关!

那么我们就可以枚举每个a,b,c,…,逐一与sum比较,就可以得出答案了。~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
int yanghui[13][13],ans[13],n,sum; bool vis[13];
int compute();
void dfs(int);
int main()
{
cin>>n>>sum;
memset(vis,true,sizeof(vis));

///初始化杨辉三角
yanghui[1][1]=1;yanghui[2][1]=1;yanghui[2][2]=1;
for(int i=3;i<=n;i++){
yanghui[i][1]=1;
for(int j=2;j<=i;j++)
yanghui[i][j]=yanghui[i-1][j-1]+yanghui[i-1][j];
}

dfs(1);///从第一个位置开始搜索

return 0;
}
int compute()///当已经枚举到了最后一位时,可以计算现在答案的值是否真的等于我们输入的sum
{
int temp=0;
for(int i=1;i<=n;i++) temp+=ans[i]*yanghui[n][i];
return temp;
}
void dfs(int pointer)
{
for(int i=1;i<=n;i++){
if(vis[i]){
ans[pointer]=i;
vis[i]=false;
if(pointer==n&&compute()==sum) {
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
break;
}

if(pointer<n) dfs(pointer+1);

ans[pointer]=0;
vis[i]=true;
}
}
}

二维DFS

上面方法的问题在于:只在所有位置都选完的时候才判定是否符合条件(sum=sum_to_find|(在这个程序中)sum=compute()),但是有很多情况下在我们没选完的时候就已经不符合条件了(sum>sum_to_find)。我们必须要排除这种情况,这样的话就必须在搜索的同时记录现在状态下合(sum)的大小,那么我们就需要写一个二维的dfs

~~实在太蠢了,把所有要判定的地方都加上&flag运行时间直接从1500ms提到了101ms ~~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
int yanghui[13][13],ans[13],n,sum_to_find; bool vis[13],flag=true;
int compute();
void dfs(int,int);
int main()
{
cin>>n>>sum_to_find;
memset(vis,true,sizeof(vis));

///初始化杨辉三角
yanghui[1][1]=1;yanghui[2][1]=1;yanghui[2][2]=1;
for(int i=3;i<=n;i++){
yanghui[i][1]=1;
for(int j=2;j<=i;j++)
yanghui[i][j]=yanghui[i-1][j-1]+yanghui[i-1][j];
}

dfs(1,0);

return 0;
}
void dfs(int pointer,int sum)
{
if(flag){///这个地方代码这么丑是因为我把所有要判定的地方都加上&flag,不然TLE
for(int i=1;i<=n;i++){
if(vis[i]&&flag){
ans[pointer]=i;
vis[i]=false;
sum+=ans[pointer]*yanghui[n][pointer];
if(pointer==n&&sum==sum_to_find&&flag) {
for(int i=1;i<=n;i++) {cout<<ans[i]<<" "; flag=false;}
break;
}

if(pointer<n&&sum<sum_to_find&&flag) dfs(pointer+1,sum);

sum-=ans[pointer]*yanghui[n][pointer];
ans[pointer]=0;///这两句的顺序可不能搞混了,要先还原sum值,再还原ans值,因为sum值和ans值有关
vis[i]=true;
}
}}
}
CATALOG
  1. 1. 暴力搜索
  2. 2. 杨辉三角
  3. 3. 二维DFS