标签:
Robert is clipping his fingernails. But the nail clipper is old and the edge of the nail clipper is potholed.
The nail clipper‘s edge is N millimeters wide. And we use N characters(‘.‘ or ‘*‘) to represent the potholed nail clipper. ‘.‘ represents 1 bad millimeter edge, and ‘*‘ represents 1 good millimeter edge.(eg. "*****" is a 5 millimeters nail clipper with the whole edge good. "***..." is a 6 millimeters nail clipper with half of its edge good and half of its edge bad.)
Notice Robert can turn over the clipper. Turning over a "**...*"-nail clipper will make a "*...**"-nail clipper.
One-millimeter good edge will cut down Robert‘s one-millimeter fingernail. But bad one will not. It will keep the one-millimeter unclipped.
Robert‘s fingernail is M millimeters wide. How many times at least should Robert cut his fingernail?
There will be multiple test cases(about 15). Please process to the end of input.
First line contains one integer N.(1≤N≤10)
Second line contains N characters only consists of ‘.‘ and ‘*‘.
Third line contains one integer M.(1≤M≤20)
One line for each case containing only one integer which is the least number of cuts. If Robert cannot clipper his fingernail then output -1.
8 ****..** 4 6 *..*** 7
1 2
We use ‘-‘ to present the fingernail. For sample 1: fingernail: ---- nail clipper: ****..** Requires one cut. For sample 2: fingernail: ------- nail clipper: *..*** nail clipper turned over: ***..* Requires two cuts.
这个题目对于我来说还是太难了,现在对dfs仅仅是知道贴上大牛代码一份,留作复习研究
解法一:
#include <stdio.h> #include <math.h> #include <string.h> #include <algorithm> using namespace std; int n,m,right,left,minn; char str[20]; int clp[20]; void dfs(int fig[],int s,int flag,int step) { int i,j,k,tem[50]; if(step>=minn) return ; if(flag) { for(i = 0; i<=25; i++) tem[i] = fig[i]; for(i = s,j = left; i<=m && j<=n; i++,j++) { if(!tem[i]) tem[i] = clp[j]; } for(i = s; i<=m; i++) { if(!tem[i]) { s = i; break; } } if(i == m+1) { if(step<minn) { minn = step; } return ; } dfs(tem,s,1,step+1); dfs(tem,s,0,step+1); } else { for(i = 0; i<=25; i++) tem[i] = fig[i]; for(i = s,j = right; i<=m && j>=1; i++,j--) { if(!tem[i]) tem[i] = clp[j]; } for(i = s; i<=m; i++) { if(!tem[i]) { s = i; break; } } if(i == m+1) { if(step<minn) { minn = step; } return ; } dfs(tem,s,0,step+1); dfs(tem,s,1,step+1); } } int main() { int i,j,k,len,fig[50]; while(~scanf("%d",&n)) { scanf("%s",str); memset(clp,0,sizeof(clp)); for(i = 0; i<n; i++) { if(str[i] == ‘*‘) clp[i+1] = 1; } left = 0; for(i = 1; i<=n; i++) if(clp[i]) { left = i; break; } for(i = n; i>=1; i--) if(clp[i]) { right = i; break; } scanf("%d",&m); if(left==0) { printf("-1\n"); continue; } minn = 999999999; memset(fig,0,sizeof(fig)); dfs(fig,1,1,1); dfs(fig,1,0,1); printf("%d\n",minn); } return 0; }
解法二:
首先把 (1<<m)-1 作为指甲没剪时的初态(全是1),dp[x]保存的就是对于每个状态,需要剪的最少次数。
当前状态x以二进制表示时,出现的1表示这一位置还留有指甲,0就是已剪去。而对于指甲钳,又可以将其以二进制表示,对于案例:****..**,不妨用11110011代替,1表示当前位置刀锋完好,0相反。于是对每一位分析:
原来指甲情况 A | 指甲钳刀锋 B | 最终指甲情况 Y |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 1 |
1 | 1 | 0 |
化简一下得到每一位上的关系:Y = A & ~B
所以递推方程就是:dp[x & (~B)] = min(dp[x & (~B)], dp[x] + 1);
问题是,指甲钳并不总是和指甲最左端对齐,所以还需要对指甲钳进行移动。反应在上式,就是对B进行左右各m次的移位操作。另外,指甲钳可以反着用,于是B还需要分正反情况考虑。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define INF 0x3f3f3f3f int dp[1 << 20]; char s[20],rev[20]; int main() { int n,m,left,right; while(~scanf("%d%s%d",&n,s,&m)) { for(int i=0;i<n;i++) rev[i]=s[n-i-1]; left=0; right=0; for(int i=0;i<n;i++) { left=left<<1; right=right<<1; if(s[i]==‘*‘) left|=1; if(rev[i]==‘*‘) right|=1; } for(int i=0;i<1<<m;i++) dp[i]=INF; dp[(1<<m)-1]=0; for(int i=(1<<m)-1;i>=0;i--) { if(dp[i]==INF)// 由i 剪到 i&(~(left<<j)的状态 若 。。 说明这还没被剪过 continue; for(int j=0;j<m;j++) { dp[i&(~(left<<j))]=min(dp[i&(~(left<<j))],dp[i]+1); dp[i&(~(left>>j))]=min(dp[i&(~(left>>j))],dp[i]+1); dp[i&(~(right<<j))]=min(dp[i&(~(right<<j))],dp[i]+1); dp[i&(~(right>>j))]=min(dp[i&(~(right>>j))],dp[i]+1); } } if(dp[0]<INF) printf("%d\n",dp[0]); else printf("-1\n"); } return 0; }
标签:
原文地址:http://www.cnblogs.com/xuhuaiqu/p/4437741.html