https://ac.nowcoder.com/acm/contest/4462/C
题目描述:
一列上有12个孔,这12个孔中有些孔被遮挡住了。
假定我们用 ‘-’ 来表示没被遮挡住的孔,用 ‘o’ 来表示被遮挡住的孔。
如果相邻的三个孔有两个孔被遮挡,并且被遮挡的两个孔相邻,就是 ‘-oo’ 和 ‘oo-’。
对于这样的三个孔,我们可以将中间的孔的遮挡物移开,代价是将一端的遮挡物移到另一端没有被遮挡的孔上面。
对于一列给定的孔,你的任务是制定操作的顺序,使得最后剩余的被遮挡的孔的个数最少,并输出最后剩余的被遮挡的孔的个数。
输入描述:
第一行输入一n,n≤10^5 。
接下来n行,每行12个字符,代表孔的状态。
思路:因为孔的个数只有12个,但询问的次数很多,所以可以将这个包含‘-’‘o’的序列进行状态压缩,转化成一个01二进制数。从第一位开始遍历“oo”,如果“oo”有一边有‘-’,就分别dfs进下一个状态。同时在dfs过程中,记录下已知情况的答案。(因为孔只有12个所以递归的次数也不会很多,不加记忆化也可以过?)
#include<cstdio>
#include<string.h>
#include<set>
#include<algorithm>
using namespace std;
//#define int long long
#define LL long long int
const int manx=1e5+100;
const int INF=0x3f3f3f3f;
int dp[manx],vs[manx];
int dfs(int x)
{
if(vs[x]||!x)
return dp[x];
int cnt=0;//维护最小最1的个数
for(int i=0;i<12;i++)
if(x&(1<<i))
++cnt;
for(int i=0;i<=10;i++)
{
if((x&(1<<i))&&(x&(1<<(i+1))))
{
if(i>=1&&!(x&(1<<(i-1))))//遇到“-oo”
cnt=min(cnt,dfs(x^(7<<(i-1))));
if(i<=9&&!(x&(1<<(i+2))))//遇到“oo-”
cnt=min(cnt,dfs(x^(7<<i)));
}
}
vs[x]=1;
return dp[x]=cnt;
}
int main()
{
int n;
char s[20];
memset(dp,0,sizeof(dp));
memset(vs,0,sizeof(vs));
scanf("%d",&n);
while(n--)
{
int x=0;
scanf("%s",s);
for(int i=0;s[i]!='\0';i++)
x=(x<<1)+(s[i]=='o'?1:0);
printf("%d\n",dfs(x));
}
return 0;
}
收集纸片:https://ac.nowcoder.com/acm/contest/4462/D(状态压缩)