WOJ 1546 Maze 图论上的状态压缩DP

http://acm.whu.edu.cn/land/problem/detail?problem_id=1546

这个题目还是聪哥教的方法过的

首先搜索是必须的,而且通过搜索来缩点,这些应该要想到,即把图上的起点和终点还有能量点进行标记,之后用bfs找到这些个点的最短路,最多只有12个点,这样对12个点进行状态压缩DP,状态压缩这部分不是很好写

用dp[i][j]表示在状态i的情况下,此刻在j点落脚的最大能量数,则先枚举i再枚举j,再枚举k,k代表已经存在于i里面的点 每次从dp[i][k]过度到dp[i][j],从dp[i][k]+dis[k][j]+dis[j][m+1]<=L(m+1是终点)则可以进行转移,取得该能量。

整个状态转移走完找出最大的能量数跟需求判断下即可得出结果

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 1<<30
using namespace std;
char g[55][55];
int vis[55][55];
int num[15];
int d[15][15];
int dp[1<<12][15];
int dis[1<<12][15];
int w,h,l,m,s;
struct node
{
    int x,y,deep;
} v[15];
int dir[][2]={{0,1},{0,-1},{1,0},{-1,0}};
void bfs(int x,int y)
{
    node sx=node{x,y,0};
    queue<node> q;
    q.push(sx);
    memset(vis,0,sizeof vis);
    char cc=g[x][y];
    while (!q.empty())
    {
        node nx=q.front();
        q.pop();
        if (vis[nx.x][nx.y]) continue;
        vis[nx.x][nx.y]=1;
        for (int i=0;i<4;i++)
        {
            node qx=(node){nx.x+dir[i][0],nx.y+dir[i][1],nx.deep+1};
            if (qx.x<0 || qx.x>=h || qx.y<0 || qx.y>=w) continue;
            if (vis[qx.x][qx.y]) continue;
            char ch=g[qx.x][qx.y];
            if (ch=='#') continue;
            if (ch>='a' && ch<='a'+m+1)
            {
                if (d[cc-'a'][ch-'a']>qx.deep)
                    d[cc-'a'][ch-'a']=qx.deep;
                //cout<<cc-'a'<<" "<<ch-'a'<<" "<<d[cc-'a'][ch-'a']<<" aaa"<<endl;
            }
            q.push(qx);
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d%d%d%d",&w,&h,&l,&m,&s);
        for (int i=1;i<=m;i++)
            scanf("%d",&num[i]);
        for (int i=0;i<h;i++){
            scanf("%s",g[i]);
        }
        int tmp=1;
        for (int i=0;i<h;i++)
            for (int j=0;j<w;j++)
             d[i][j]=INF;
        for (int i=0;i<h;i++)
        {
            for (int j=0;j<w;j++)
            {
                if (g[i][j]=='$')
                  g[i][j]='a';
                if (g[i][j]=='@')
                  g[i][j]='a'+(tmp++);
                if (g[i][j]=='<')
                  g[i][j]='a'+m+1;
            }
        }
        for (int i=0;i<=h;i++)
        {
            for (int j=0;j<=w;j++)
            {
                if (g[i][j]>='a' && g[i][j]<='a'+m+1){
                // cout<<i<<" "<<j<<endl;
                 bfs(i,j);
//进行BFS缩点                 //cout<<endl;
                }
            }
        }
        int ret=0;

//对可以直接取得点先取了
for (int i=1;i<=m;i++) { if (d[i][m+1]<=l/2) { ret+=num[i]; num[i]=0; } } int ans=0; memset(dp,-1,sizeof dp); memset(dis,0,sizeof dis); for (int i=0;i<m;i++) { dis[1<<i][i+1]=d[0][i+1]; if (d[0][i+1]+d[i+1][m+1]<=l){ ans=max(ans,num[i+1]); dp[1<<i][i+1]=num[i+1]; } } for (int i=1;i<(1<<m);i++) { for (int j=1;j<=m;j++) { if (!(1<<(j-1) & i)) continue; int tmp=i-(1<<j-1); if (tmp==0) continue; int td=d[j][m+1]; for (int k=1;k<=m;k++) { if (dp[tmp][k]==-1) continue; if (!(tmp&(1<<k-1))) continue; if (dis[tmp][k]+d[k][j]<=l-td) { if (dp[i][j]==-1) { dp[i][j]=dp[tmp][k]+num[j]; dis[i][j]=dis[tmp][k]+d[k][j]; } else if (dp[i][j]<dp[tmp][k]+num[j]) { dp[i][j]=dp[tmp][k]+num[j]; dis[i][j]=dis[tmp][k]+d[k][j]; } } } ans=max(ans,dp[i][j]); } } if (ret+ans<s || d[0][m+1]>l) puts("NO"); else puts("YES"); } return 0; }

 

posted @ 2014-04-04 21:45  KRisen  阅读(163)  评论(0编辑  收藏  举报