挖地雷(记忆化搜索)

Posted leaf-2234

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了挖地雷(记忆化搜索)相关的知识,希望对你有一定的参考价值。

原题:

挖地雷

时间限制: 1 Sec  内存限制: 125 MB

题目描述

在一个地图上有N个地窖(N≤20),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。

输入格式

有若干行。

第1行只有一个数字,表示地窖的个数N。

第2行有N个数,分别表示每个地窖中的地雷个数。

第3行至第N+1行表示地窖之间的连接情况:

第3行有n-1个数(0或1),表示第一个地窖至第2个、第3个、…、第n个地窖有否路径连接。如第33行为1 1 0 0 0 … 0,则表示第1个地窖至第2个地窖有路径,至第3个地窖有路径,至第4个地窖、第5个、…、第n个地窖没有路径。

第4行有n?2个数,表示第二个地窖至第3个、第4个、…、第n个地窖有否路径连接。

… …

第n+1行有1个数,表示第n-1个地窖至第n个地窖有否路径连接。(为0表示没有路径,为1表示有路径)。

输出格式

有两行

第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。

第二行只有一个数,表示能挖到的最多地雷数。

 

输入输出样例

输入

5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1

输出

1 3 4 5
27

 


 

 

题意:

  给定一张单向图(注意单向!否则你练样例都过不了),每个顶点有一个点权,求从其中任意一个顶点出发不回头,最多能获得的权值总和。

 

  Generally speaking,DP题我们会先考虑DFS的做法(如果你对DP不太了解,请点这里)。

  让我们先把路径的处理放一边去。

  先用二维数组g记录每两个地窖之间能否到达,从每一个顶点出发对所有能遍历的边都尝试一遍,记录最大值,代码如下(直接提交后果自负):

 1 #include<iostream>
 2 #include<stdio.h>
 3 using namespace std;
 4 int n,ans,a[25],mark[25],g[25][25];
 5 void dfs(int x,int rec)//简洁易懂的dfs 
 6 {
 7     ans=max(ans,rec);
 8     for(int i=1;i<=n;i++)
 9     {
10         if(g[x][i] && !mark[i])
11         {
12             mark[i]=1;
13             dfs(i,rec+a[i]);//搜索与回溯 
14             mark[i]=0;
15         }
16     }
17 }
18 int main()
19 {
20     scanf("%d",&n);
21     for(int i=1;i<=n;i++)  scanf("%d",&a[i]);
22     for(int i=1;i<n;i++)
23     {
24         for(int j=i+1;j<=n;j++)
25         {
26             int x;
27             scanf("%d",&x);
28             if(x)  g[i][j]=1;//注意图是单向的 
29         }    
30     }
31     for(int i=1;i<=n;i++)
32     {
33         mark[i]=1;
34         dfs(i,a[i]);
35         mark[i]=0;//顶点逐个尝试 
36     }
37     printf("%d",ans);
38 return 0;
39 } 

  上面那个代码非常简洁易懂,但仔细观察,我们会发现dfs中的参数rec并没有使用到运算中,所以我们可以做一个小小的优化(注意,时间复杂度并没有改变,只是方便后面的记忆化搜索):

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 int n,ans,a[25],mark[25],g[25][25];
 6 int dfs(int x)
 7 {
 8     int rec=0;
 9     for(int i=1;i<=n;i++)
10     {
11         if(!mark[i] && g[x][i])
12         {
13             mark[i]=1;
14             rec=min(rec,dfs(i));//rec记录这个顶点出发能到达的最大值 
15             mark[i]=0;
16         }
17     }
18 return rec+a[x];//返回rec与当前的地雷数量 
19 }
20 int main()
21 {
22     scanf("%d",&n);
23     for(int i=1;i<=n;i++)  scanf("%d",&a[i]);
24     for(int i=1;i<n;i++)
25     {
26         for(int j=i+1;j<=n;j++)
27         {
28             int x;
29             scanf("%d",&x);
30             if(x)  g[i][j]=1;
31         }    
32     }
33     for(int i=1;i<=n;i++)
34     {
35         mark[i]=1;
36         ans=min(ans,dfs(i));
37         mark[i]=0;
38     }
39     printf("%d",ans);
40 return 0;
41 } 

  但是这样的代码直接提交的话仍然会超时。为什么呢?很容易想到,我们在搜索的时候有很多的时间都在重复搜索,因此优化的办法是每找到当前点出发能到达的最大值就记录一下,下面可以直接调用。

  路径储存的代码也放到完整代码中了(因为我太菜了讲不清楚,也许模拟一下会帮助理解)。

  完整代码来啦

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<string.h>
 4 using namespace std;
 5 int n,ans,a[25],mark[25],g[25][25];
 6 int dp[25],pre[25],pos;
 7 int dfs(int x)
 8 {
 9     if(dp[x]!=-1)  return dp[x];
10     int rec=0;
11     for(int i=1;i<=n;i++)
12     {
13         if(!mark[i] && g[x][i])
14         {
15             mark[i]=1;
16             if(dfs(i)>rec)  rec=dfs(i),pre[x]=i;
17             mark[i]=0;
18         }
19     }
20     dp[x]=rec+a[x];
21 return dp[x];
22 }
23 int main()
24 {
25     memset(dp,-1,sizeof dp);
26     scanf("%d",&n);
27     for(int i=1;i<=n;i++)  scanf("%d",&a[i]);
28     for(int i=1;i<n;i++)
29     {
30         for(int j=i+1;j<=n;j++)
31         {
32             int x;
33             scanf("%d",&x);
34             if(x)  g[i][j]=1;
35         }    
36     }
37     for(int i=1;i<=n;i++)
38     {
39         mark[i]=1;
40         if(ans<dfs(i))  ans=dfs(i),pos=i;
41         mark[i]=0;
42     }
43     int now=pre[pos];
44     printf("%d",pos);
45     while(now)
46     {
47         printf(" %d",now);
48         now=pre[now];
49     }
50     printf("
");
51     printf("%d",ans);
52 return 0;
53 } 

 







以上是关于挖地雷(记忆化搜索)的主要内容,如果未能解决你的问题,请参考以下文章

挖地雷问题

挖地雷 题解

挖地雷 题解

P2196 挖地雷

P2196 挖地雷

挖地雷