• 洛谷P2566[SCOI2009]围豆豆


    题目描述:

    Input第一行两个整数N和M,为矩阵的边长。 第二行一个整数D,为豆子的总个数。 第三行包含D个整数V1到VD,分别为每颗豆子的分值。 接着N行有一个N×M的字符矩阵来描述游戏矩阵状态,0表示空格,#表示障碍物。而数字1到9分别表示对应编号的豆子。

    Output仅包含一个整数,为最高可能获得的分值。

    Sample Input

    3 8 3

    30 -100 30

    00000000

    010203#0

    00000000

    Sample Output

    38

    数据范围:

    50%的数据满足1≤D≤3。
    100%的数据满足1≤D≤9,1≤N, M≤10,-10000≤Vi≤10000。

    思路分析:

      我们一看到这么小的数据应该能想到要用DP,关键是怎么DP;

    我们先说一下如何判断一个豆豆被围住,这需要用到“射线法”;

    我们可以举几个例子来说,如上图所示(画得这么丑不要介意)我们发现,当从‘豆豆’向右延伸出一条直线时,如果与我们的路径有奇数个交点,那么豆豆将在路径所围成的圈内,否则在圈外,但还有一种特殊情况需要考虑:

     向右的直线和我们的路径有交时,会出现有偶数个交点(如图中)而在路径内的情况(我们计算交点数的方式是往下走时若过豆豆的纵坐标则记一次,所以中间横在紫线上的点不算,上图只有一左一右两个交点)

    解决了判断的问题,我们便可以思考如何DP了

    大致思路是,考虑Spfa的做法,从一个点出发,遍历所有与他相连的点,若合法则压入队内,一个点只走一次,知道队列为空,统计答案即可。如何找到与已知点相邻的点的坐标呢,我们在学栈时曾做过一道叫做细胞个数的题,我们可以从那里得到启发,即开两个数组,分别为

    f1[4]={1,-1,0,0},f2[4]={0,0,1,-1},之后让已知点的坐标加上对应的值即可修改其位置。若i<2(即已知点上下移动),统计一下是否有点向右的直线与它有奇数个交点,有的话则在原来已知点的基础上加上这个豆豆的权值;我们用dp第一位和第二位表示点的坐标,第三位表示状态,如果一个点的一个状态合法(即vis为true,遍历过)则拿他和当前最大答案比较,最后输出最大答案。其他细节详见注释。

    附上代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<queue>
     5 #include<map>
     6 using namespace std;
     7 struct Node2{  //开一个结构体方便用队列存储 
     8     int x,y,dis;
     9     Node2(int a,int b,int c){
    10         x=a;y=b;dis=c;
    11     }
    12 };
    13 int vis[11][11][1050],a[11][11],val[11];  
    14 int n,m,cnt;
    15 int dp[11][11][1050],x0[11],y0[11],ans=0;
    16 //vis表示当前点当前状态是否计算过,val记录每个豆豆的权值,
    17 //x0,y0分别存储豆豆的横,纵坐标,温馨提示:千万不要定义y1
    18 //a数组记录棋盘,dp[i][j][k]记录坐标为(i,j)的点状态为k时权值. 
    19 int f1[4]={1,-1,0,0}; //用于已知点的移动 
    20 int f2[4]={0,0,1,-1};
    21 void Spfa(int x,int y){
    22     memset(vis,0,sizeof(vis));  //需要跑多次,记得初始化 
    23     memset(dp,0,sizeof(dp));
    24     dp[x][y][0]=0;
    25     queue<Node2>q;
    26     q.push(Node2(x,y,0));
    27     int tx,ty,tz,tdp,cross;
    28     //tx,ty,tz表示新点的横,纵坐标及状态
    29     //tdp表示当前状态权值,cross表示豆豆横坐标 
    30     while(!q.empty()){
    31         Node2 t=q.front();q.pop();
    32         for(int i=0;i<4;++i){
    33             tx=t.x+f1[i];ty=t.y+f2[i];
    34             if(tx<=0||ty<=0||tx>n||ty>m||a[tx][ty]!=0) //判断合法 
    35                 continue;
    36             tz=t.dis;tdp=dp[t.x][t.y][t.dis]-1;//由于又走了一步,分数要减一 
    37             if(i<2){  //判断是否加分 
    38                 cross=min(tx,t.x);
    39                 for(int j=1;j<=cnt;++j){
    40                     if(x0[j]!=cross||y0[j]>ty) //和豆豆的向右只限无交点 
    41                         continue;
    42                     if(tz & (1<<(j-1))) tdp-=val[j]; //如果原来加过,再过一次时偶数变奇数,要减去 
    43                     else tdp+=val[j];    //反之加上 
    44                     tz=tz^(1<<(j-1));   //经过次数加一次 
    45                 }
    46             }
    47             if(!vis[tx][ty][tz]){ //入队,准备下一次循环 
    48                 dp[tx][ty][tz]=tdp;
    49                 q.push(Node2(tx,ty,tz));
    50                 vis[tx][ty][tz]=1;
    51             }
    52          }    
    53     }
    54     for(int i=0;i<(1<<cnt);++i)  //统计当前点所有状态的答案 
    55         if(vis[x][y][i])
    56             ans=max(ans,dp[x][y][i]);
    57 }
    58 int main(){
    59     //freopen("a.txt","r",stdin);
    60     scanf("%d%d%d",&n,&m,&cnt);
    61     for(int i=1;i<=cnt;++i)
    62         scanf("%d",&val[i]);
    63     for(int i=1;i<=n;++i)
    64     for(int j=1;j<=m;++j){
    65         char c=' ';
    66         while(c==' '||c=='
    ')
    67             scanf(" %c",&c);
    68         if(c=='#') a[i][j]=-1; //构建棋盘 
    69         if(c>='0'&&c<='9'){
    70             x0[c-'0']=i;y0[c-'0']=j; //存储豆豆 
    71             a[i][j]=c-'0';
    72         }
    73     }
    74     for(int i=1;i<=n;++i)
    75     for(int j=1;j<=m;++j){
    76         if(a[i][j]==0)
    77             Spfa(i,j);//从每个点出发跑一遍 
    78     }
    79     printf("%d
    ",ans);
    80     return 0;
    81 }
    View Code
  • 相关阅读:
    Xamarin studio配置问题
    安装moogodb
    什么是作用域, 什么是闭包, 什么是原型链,什么是递归, 什么是函数函数声明,什么是函数表达式,继承,this 指向
    前端框架Vue、angular、React的优点和缺
    判断终端是IOS 或 是Android
    VS code终端错位
    安装Flutter
    web直传OSS设置 头部信息【坑】
    从零创建react项目
    webpack报错vue-template-compiler 版本不对
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/12704248.html
Copyright © 2020-2023  润新知