NOJ 1111 保险箱的密码 【大红】 [区间dp]

传送门

保险箱的密码 【大红】

时间限制(普通/Java) : 1000 MS/ 3000 MS          运行内存限制 : 65536 KByte
总提交 : 118            测试通过 : 3 

题目描述

最近sed同学设计了一套保险箱密码锁,密码锁上有依次排好的0、1数字键,保险箱密码是由0和1组成的数字串。开启这个保险箱需要正确的密码,还需要将密码锁上数字键设定为对应的0或1,而这个过程需要特定的技巧:每次变换的工作:将密码锁上连续的0、1数字串用同样数目的全0或全1数字串代替。现给你正确的密码 (不超过200),请计算开启这个保险箱,最少需要经过多少次变换。

输入

第一行是一个正整数:测试用例数目,最多为100。之后,每个测试用例包括两行:

l       第1行给出密码锁上初始的0、1数字串(不超过200)

l       第2行给出保险箱的正确密码

输出

对于每个测试用例:

l       开启保险箱需要的最少变换数

样例输入

2
000
111
1011
0010

样例输出

1
2

题目来源

“IBM南邮杯”团队赛2009


njczy2010

1111

Accepted

953MS

  724K

2732Byte

G++

2015-02-10 10:20:01.0

njczy2010

1111

Time Limit Exceed at Test 1
   
2724Byte

G++

2015-02-10 10:18:11.0

题解:

区间dp

对于 i-j的区间,整个区间置0或置1,至多只会出现一次
因为你如果再置,就会把前面的操作覆盖,没有意义
所以,下面的操作长度,必然都小于 i-j

同时可以证明,重叠的区间操作也是没有意义的,同理:会覆盖前面的某些操作
所以,可以不断把区间缩小

加上记忆化就可以水过了,不过应该还有更加优化的方法,有待思考。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cstdio>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<map>
  9 #include<set>
 10 #include<stack>
 11 #include<string>
 12
 13 #define N 205
 14 #define M 105
 15 #define mod 10000007
 16 //#define p 10000007
 17 #define mod2 1000000000
 18 #define ll long long
 19 #define LL long long
 20 #define eps 1e-6
 21 #define inf 100000000
 22 #define maxi(a,b) (a)>(b)? (a) : (b)
 23 #define mini(a,b) (a)<(b)? (a) : (b)
 24
 25 using namespace std;
 26
 27 int T;
 28 char s[N];
 29 char t[N];
 30 int dp[N][N];
 31 int dp1[N][N];
 32 int dp0[N][N];
 33 int le;
 34 int ans;
 35
 36 int fun(int l,int r);
 37 int fun0(int l,int r);
 38 int fun1(int l,int r);
 39
 40 void ini()
 41 {
 42     memset(dp,-1,sizeof(dp));
 43     memset(dp1,-1,sizeof(dp1));
 44     memset(dp0,-1,sizeof(dp0));
 45     scanf("%s",s+1);
 46     scanf("%s",t+1);
 47     le=strlen(s+1);
 48 }
 49
 50 int fun1(int l,int r)
 51 {
 52     if(dp1[l][r]!=-1) return dp1[l][r];
 53     int re;
 54     int st=l;
 55     int en=r;
 56     for(st=l;st<=r;st++){
 57         if(t[st]==‘0‘) break;
 58     }
 59     if(st>r) return dp1[l][r]=0;
 60     for(en=r;en>=st;en--){
 61         if(t[en]==‘0‘) break;
 62     }
 63     if(st==en){
 64         return dp1[l][r]=1;
 65     }
 66     re=fun0(st,en)+1;
 67     dp1[l][r]=re;
 68     return re;
 69 }
 70
 71 int fun0(int l,int r)
 72 {
 73     if(dp0[l][r]!=-1) return dp0[l][r];
 74     int st=l;
 75     int en=r;
 76     int re;
 77     for(st=l;st<=r;st++){
 78         if(t[st]==‘1‘) break;
 79     }
 80     if(st>r) return dp0[l][r]=0;
 81     for(en=r;en>=st;en--){
 82         if(t[en]==‘1‘) break;
 83     }
 84     if(st==en){
 85         return dp0[l][r]=1;
 86     }
 87     re=fun1(st,en)+1;
 88     dp0[l][r]=re;
 89     return re;
 90 }
 91
 92 int fun(int l,int r)
 93 {
 94     if(dp[l][r]!=-1) return dp[l][r];
 95     int st=l;
 96     int en=r;
 97     for(st=l;st<=r;st++){
 98         if(s[st]!=t[st]) break;
 99     }
100     if(st>r) return dp[l][r]=0;
101     for(en=r;en>=st;en--){
102         if(s[en]!=t[en]) break;
103     }
104     if(st==en){
105         return dp[l][r]=1;
106     }
107     int i;
108     int re=1000000000;
109     for(i=st+1;i<=en;i++){
110         re=min(re,fun(st,i-1)+fun(i,en));
111     }
112     re=min(re,fun1(st,en)+1);
113     re=min(re,fun0(st,en)+1);
114     dp[l][r]=re;
115     return re;
116 }
117
118 void solve()
119 {
120     ans=fun(1,le);
121 }
122
123 void out()
124 {
125     /*
126     int i,j;
127     for(i=1;i<=le;i++){
128         for(j=i;j<=le;j++){
129             printf(" i=%d j=%d dp1=%d dp0=%d dp=%d\n",i,j,dp1[i][j],dp0[i][j],dp[i][j]);
130         }
131     }*/
132     printf("%d\n",ans);
133 }
134
135 int main()
136 {
137     //freopen("data.in","r",stdin);
138     //freopen("data.out","w",stdout);
139     scanf("%d",&T);
140     //for(int ccnt=1;ccnt<=T;ccnt++)
141     while(T--)
142     //scanf("%d%d",&n,&m);
143     //while(scanf("%s",s)!=EOF)
144     {
145         ini();
146         solve();
147         out();
148     }
149     return 0;
150 }

继续思考,将区间分成两部分时,原串和正确串已经相同的部分可以跳过,故可以减少好多操作,当成剪枝1


njczy2010

1111

Accepted

375MS

  724K

2924Byte

G++

2015-02-10 10:48:12.0

njczy2010

1111

Wrong Answer at Test 1
   
2738Byte

G++

2015-02-10 10:33:44.0

njczy2010

1111

Accepted

953MS

  724K

2732Byte

G++

2015-02-10 10:20:01.0

njczy2010

1111

Time Limit Exceed at Test 1
   
2724Byte

G++

2015-02-10 10:18:11.0
  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cstdio>
  5 #include<algorithm>
  6 #include<cmath>
  7 #include<queue>
  8 #include<map>
  9 #include<set>
 10 #include<stack>
 11 #include<string>
 12
 13 #define N 205
 14 #define M 105
 15 #define mod 10000007
 16 //#define p 10000007
 17 #define mod2 1000000000
 18 #define ll long long
 19 #define LL long long
 20 #define eps 1e-6
 21 #define inf 100000000
 22 #define maxi(a,b) (a)>(b)? (a) : (b)
 23 #define mini(a,b) (a)<(b)? (a) : (b)
 24
 25 using namespace std;
 26
 27 int T;
 28 char s[N];
 29 char t[N];
 30 int dp[N][N];
 31 int dp1[N][N];
 32 int dp0[N][N];
 33 int le;
 34 int ans;
 35
 36 int fun(int l,int r);
 37 int fun0(int l,int r);
 38 int fun1(int l,int r);
 39
 40 void ini()
 41 {
 42     memset(dp,-1,sizeof(dp));
 43     memset(dp1,-1,sizeof(dp1));
 44     memset(dp0,-1,sizeof(dp0));
 45     scanf("%s",s+1);
 46     scanf("%s",t+1);
 47     le=strlen(s+1);
 48 }
 49
 50 int fun1(int l,int r)
 51 {
 52     if(dp1[l][r]!=-1) return dp1[l][r];
 53     int re;
 54     int st=l;
 55     int en=r;
 56     for(st=l;st<=r;st++){
 57         if(t[st]==‘0‘) break;
 58     }
 59     if(st>r) return dp1[l][r]=0;
 60     for(en=r;en>=st;en--){
 61         if(t[en]==‘0‘) break;
 62     }
 63     if(st==en){
 64         return dp1[l][r]=1;
 65     }
 66     re=fun0(st,en)+1;
 67     dp1[l][r]=re;
 68     return re;
 69 }
 70
 71 int fun0(int l,int r)
 72 {
 73     if(dp0[l][r]!=-1) return dp0[l][r];
 74     int st=l;
 75     int en=r;
 76     int re;
 77     for(st=l;st<=r;st++){
 78         if(t[st]==‘1‘) break;
 79     }
 80     if(st>r) return dp0[l][r]=0;
 81     for(en=r;en>=st;en--){
 82         if(t[en]==‘1‘) break;
 83     }
 84     if(st==en){
 85         return dp0[l][r]=1;
 86     }
 87     re=fun1(st,en)+1;
 88     dp0[l][r]=re;
 89     return re;
 90 }
 91
 92 int fun(int l,int r)
 93 {
 94     if(dp[l][r]!=-1) return dp[l][r];
 95     int st=l;
 96     int en=r;
 97     for(st=l;st<=r;st++){
 98         if(s[st]!=t[st]) break;
 99     }
100     if(st>r) return dp[l][r]=0;
101     for(en=r;en>=st;en--){
102         if(s[en]!=t[en]) break;
103     }
104     if(st==en){
105         return dp[l][r]=1;
106     }
107     int re=1000000000;
108     int en1,st2;
109     for(en1=st;en1<=en-1;en1++){
110         if(s[en1]==t[en1]) continue;
111         for(st2=en1+1;st2<=en;st2++){
112             if(s[st2]==t[st2]) continue;
113             re=min(re,fun(st,en1)+fun(st2,en));
114             en1=st2-1;
115             break;
116         }
117     }
118     re=min(re,fun1(st,en)+1);
119     re=min(re,fun0(st,en)+1);
120     dp[l][r]=re;
121     return re;
122 }
123
124 void solve()
125 {
126     ans=fun(1,le);
127 }
128
129 void out()
130 {
131     /*
132     int i,j;
133     for(i=1;i<=le;i++){
134         for(j=i;j<=le;j++){
135             printf(" i=%d j=%d dp1=%d dp0=%d dp=%d\n",i,j,dp1[i][j],dp0[i][j],dp[i][j]);
136         }
137     }*/
138     printf("%d\n",ans);
139 }
140
141 int main()
142 {
143     //freopen("data.in","r",stdin);
144     //freopen("data.out","w",stdout);
145     scanf("%d",&T);
146     //for(int ccnt=1;ccnt<=T;ccnt++)
147     while(T--)
148     //scanf("%d%d",&n,&m);
149     //while(scanf("%s",s)!=EOF)
150     {
151         ini();
152         solve();
153         out();
154     }
155     return 0;
156 }
时间: 2024-08-15 06:05:17

NOJ 1111 保险箱的密码 【大红】 [区间dp]的相关文章

nyoj 1111 游戏人生 【区间dp】

题目:nyoj 1111 游戏人生 题意:在一条直线上有n只狼,每只狼有自己的攻击力和辅助攻击,辅助攻击对旁边的两只狼有,问怎样的一个杀怪顺序才能使得花费最小. 分析:这是北京现场赛的题目,当时一眼看出来是区间dp,但是没有考虑计算一个区间之后两边的辅助攻击,也是很有没有做dp题目了. 定义:dp[i][j]为打从区间 i -- j 的怪的伤害,然后转移方程dp[i][j] = min(dp[i][j] ,dp[i][k-1] + a[k] + dp[k+1][j]) + fu[i-1][j+1

POJ 2955 Brackets (区间DP)

题意:给定一个序列,问你最多有多少个合法的括号. 析:区间DP,dp[i][j] 表示在 第 i 到 第 j 区间内最多有多少个合法的括号. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <ios

LightOJ1044 Palindrome Partitioning(区间DP+线性DP)

问题问的是最少可以把一个字符串分成几段,使每段都是回文串. 一开始想直接区间DP,dp[i][j]表示子串[i,j]的答案,不过字符串长度1000,100W个状态,一个状态从多个状态转移来的,转移的时候要枚举,这样时间复杂度是不可行的. 然后我就想降维度了,只能线性DP,dp[i]表示子串[0,i]的答案.这样可以从i-1转移到i,str[i]单独作一段或者str[i]能和前面的组成回文串,方程如下: dp[i]=min(dp[i-1]+1,dp[j-1]+1) (子串[j,i]是回文串) 现在

HDU 4745 Two Rabbits (区间DP)

题意:给定一个圆形的环,有两个只兔子,一只顺时针跳,一个逆时针,但每次跳到的石头必须一样,问你最多能跳多少轮. 析:本来以为是LCS呢,把那个序列看成一个回文,然后就能做了,但是时间受不了.其实是一个区间DP,dp[i[j] 表示从 i 到 j 中最长的回文数. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #in

整数划分之四 【区间dp】讲解于思考方法

一个老生长谈的问题 给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积 区间dp: 思路:先求出n从 i-j数位的 值 分析 m段的情况 4位的数  定义dp[i][j]为讲 i 分为 j 段 的最大乘积值 为了更加直观 我颠倒了一下i j 让大家看得明白些 0        1         2       3  //       i j 0    1        11      111    1111 1    0        1   

HihoCOder1323 : 回文字符串(区间DP)

回文字符串 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个字符串 S ,最少需要几次增删改操作可以把 S 变成一个回文字符串? 一次操作可以在任意位置插入一个字符,或者删除任意一个字符,或者把任意一个字符修改成任意其他字符. 输入 字符串 S.S 的长度不超过100, 只包含'A'-'Z'. 输出 最少的修改次数. 样例输入 ABAD 样例输出 1 区间DP水题,见铺垫:密码脱落. #include<cstdio> #include<cstdlib

【专题】区间dp

1.[nyoj737]石子合并 传送门:点击打开链接 描述    有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆.求出总的代价最小值. 输入 有多组测试数据,输入到文件结束.每组测试数据第一行有一个整数n,表示有n堆石子.接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开 输出 输出总代价的最小值,占单独的一行 样例输入 3 1 2 3

uva 10003 Cutting Sticks 简单区间dp

// uva 10003 Cutting Sticks 区间dp // 经典的区间dp // dp(i,j)表示切割小木棍i-j所需要的最小花费 // 则状态转移为dp(i,j) = min{dp(i,k) + dp(k,j) + a[j]-a[i]) // 其中k>i && k<j // a[j] - a[i] 为第一刀切割的代价 // a[0] = 0,a[n+1] = L; // dp数组初始化的时候dp[i][i+1]的值为 0,这表示 // 每一段都已经是切割了的,不

黑书例题 Fight Club 区间DP

题目可以在bnuoj.soj等OJ上找到. 题意: 不超过40个人站成一圈,只能和两边的人对战.给出任意两人对战的输赢,对于每一个人,输出是否可能是最后的胜者. 分析: 首先序列扩展成2倍,破环成链. dp[i][j]表示i和j能够相遇对打,那么dp[i][i+n]为真代表可以成为最后胜者. 枚举中间的k,若i和j都能和k相遇,且i和j至少一人能打赢k,那么i和j可以相遇. 复杂度o(n^3) 1 #include<cstdio> 2 #include<cstring> 3 usi