题目链接:
http://codeforces.com/problemset/problem/812/B
题意:
某一个楼有N层,每一层层M+2个房间,第一个房间和最后一个房间表示的是左楼梯和右楼梯。现在管理员处于最底层的左楼梯处,管理员关灯的时候有两个原则:
1.必须要将一层楼的灯全部关闭以后,才可以去上面的楼层。
2.管理员每次移动一个位置的时间均为1S,并且关灯的动作时不算时间的,但是上楼梯算1S。当把灯全部关闭以后,不用再次回到初始的位置。
问管理员移动需要的最短时间。
题解:
比较水的dp,需要一些预处理。
代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define met(a,b) memset(a,b,sizeof(a)) #define inf 0x3f3f3f3f char s[15+10][100+10]; int mp[15+10][100+10]; int dp[15+10][2];//表示的是当到达I层的时候左右楼梯的最小的时间 struct node { int ans; int pos1,pos2; }num[15+10];//对数据进行处理,处理出每一层所需要花费的时间以及每一层1的开始位置和1的结束位置 int main() { int n,m; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%s",s[i]); met(mp,0); for(int i=0;i<n;i++) for(int j=0;j<m+2;j++) mp[i][j]=s[i][j]-'0'; met(num,0); for(int i=0;i<n;i++) { int cnt=0; for(int j=0;j<m+2;j++) { if(mp[i][j]==1) cnt++; } num[i].ans=cnt; } for(int i=n-1;i>=0;i--) { int flag=0; int cnt=num[i].ans; for(int j=0;j<m+2;j++) { if(mp[i][j]==1) { if(!flag) { flag=1; num[i].pos1=j; } cnt--; if(cnt==0) { num[i].pos2=j; break; } } } } // 对于一些特殊情况的判断。 if(n==1) { printf("%d\n",num[0].pos2); return 0; } else if(n==2&&num[0].ans==0) { printf("%d\n",num[1].pos2); return 0; } int pos=0; // 处理出最顶层的位置 for(int i=0;i<n;i++) { if(num[i].ans!=0) { pos=i; break; } } for(int i=0;i<n;i++) { if(num[i].ans==0) { num[i].pos1=m+1; num[i].pos2=0; } } met(dp,inf); // dp的初始化 dp[n-1][0]=num[n-1].pos2*2; dp[n-1][1]=m+1; if(pos==n-1) { printf("%d\n",num[n-1].pos2); return 0; } for(int i=n-2;i>=pos;i--) { if(i!=pos) { dp[i][0]=min(dp[i+1][1]+m+1,dp[i+1][0]+num[i].pos2*2); dp[i][1]=min(dp[i+1][0]+m+1,dp[i+1][1]+(m+1 -num[i].pos1)*2); } else { dp[i][0]=min(dp[i+1][1]+(m+1-num[i].pos1),dp[i+1][0]+num[i].pos2); dp[i][1]=min(dp[i+1][1]+(m+1-num[i].pos1),dp[i+1][0]+num[i].pos2); } } int ans=min(dp[pos][0],dp[pos][1]); if(ans==0) printf("0\n"); else printf ("%d\n",ans+n-1-pos); // 最后还是要加上上楼的时间 }