zoukankan      html  css  js  c++  java
  • 挖地雷(记忆化搜索)

    原题:

    挖地雷

    时间限制: 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 } 
  • 相关阅读:
    党报
    一个人只有敢于承担责任,才有可能被赋予更大的责任。做不
    勇于担当:好男人的三块责任田——
    关于担当
    领导干部要勇于担当
    福布斯专访阿里蔡崇信:马云的坚持和改变
    阿里股权
    ContentProvider
    搞笑段子
    报业
  • 原文地址:https://www.cnblogs.com/leaf-2234/p/13874268.html
Copyright © 2011-2022 走看看