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; }