题目大意:
给定两个长度为N的01字符串,每一次选取其中的一个依次从左往右读,需要保证已经读过的0和1的个数差小于等于k,试问:这样的读字符串方案是否存在?如果存在,第一个字符串从左往右读一位输出1,第二个字符串从左往右读一位输出2,输出字典序最小的输出方案。
方法1:DFS
这个想法比较自然。首先构造出搜索图map[maxn][maxn]。定义扫描状态(i,j)表示当前第一个字符串扫描到了第i位,第二个字符串扫描到了第j位。(i,j)合法当且仅当当前状态下已经扫描过的01个数差小于等于k。如果(i,j)合法,置map[i][j] = 1;否则map[i][j] = 0。这样就得到了一张由01组成的图。以测试数据
4 1
0011
0110 为例。得到的搜索图map为:
1 1 1 1 1
1 0 1 1 1
0 0 0 1 0
1 0 1 1 1
1 1 1 1 1
现在整个问题转化为找到一条可行的“1”路径,使得一个点能够从图的左上角移动到右下角。注意题目要求字典序最小的输出方案,所以深搜时以“向下”移动优先。
注意深搜的过程。已经回溯过的点肯定不用再走,所以置一个visit矩阵有助于减小搜索量。
1: /*
2: sample output:
3: 22121112
4: Poor Alice
5: */
6: #include <iostream>
7: #include <cstdio>
8: #include <cstring>
9: #include <cmath>
10: using namespace std;
11: const int maxn = 1010;
12: char str1[maxn], str2[maxn], result[2*maxn];
13: bool map[maxn][maxn];
14: int cnt1[maxn],cnt2[maxn];
15: bool visit[maxn][maxn];
16: int n,k;
17: bool flag;
18: void dfs(int u, int v)
19: {
20: if(n == v && n == u)
21: {
22: flag = true;
23: printf("%s\n",result);
24: return;
25: }
26: if(map[u+1][v] == 1 && !visit[u+1][v] && !flag)
27: {
28: //if(flag) return;
29: result[u+v] = '1';
30: dfs(u+1,v);
31: }
32: if(map[u][v+1] == 1 && !visit[u][v+1] && !flag)
33: {
34: //if(flag) return;
35: result[u+v] = '2';
36: dfs(u,v+1);
37: }
38: visit[u][v] = 1;
39: if(u==0 && v == 0 && !flag) printf("Poor Alice\n");
40: return;
41: }
42:
43: int main()
44: {
45: //freopen("test.txt","r",stdin);
46: while(scanf("%d%d",&n,&k)!= EOF)
47: {
48: memset(map,0,sizeof(map));
49: scanf("%s%s",str1,str2);
50: cnt1[0] = 0;
51: cnt2[0] = 0;
52: for(int i=0;i<n;++i)
53: {
54: if(str1[i] == '1')
55: cnt1[i+1] = cnt1[i] + 1;
56: else cnt1[i+1] = cnt1[i] - 1;
57: if(str2[i] == '1')
58: cnt2[i+1] = cnt2[i] + 1;
59: else cnt2[i+1] = cnt2[i] - 1;
60: }
61: for(int i=0;i<=n;++i)
62: for(int j = 0;j<=n;++j)
63: {
64: int temp = cnt1[i] + cnt2[j];
65: if(abs((double)temp) <= k)
66: map[i][j] = 1;
67: }
68: result[2*n-1] = '\0';
69: /*for(int i=0;i<=n;++i)
70: {
71: for(int j=0;j<=n;++j)
72: cout<<map[i][j]<<' ';
73: cout<<endl;
74: }*/
75: flag = false;
76: memset(visit,0,sizeof(visit));
77: dfs(0,0);
78: }
79: return 0;
80: }
方法二:DP。
令ok[i][j]=1表示从第一个字符串的第i个字符,第二个字符串的第j个字符开始可以扫描完整个字符串。那么可以建立DP递归方程:
ok[i][j-1] = 1 iff ok[i][j] && map[i][j-1]
ok[i-1][j] = 1 iff ok[i][j] && map[i-1][j]
这个方法没有方法一自然,因为逆推不太容易想到。
1: #include <iostream>
2: #include <cstdio>
3: #include <cstring>
4: #include <cmath>
5: using namespace std;
6: const int maxn = 1010;
7: char str1[maxn], str2[maxn];
8: bool map[maxn][maxn],ok[maxn][maxn];
9: int cnt1[maxn],cnt2[maxn];
10: bool visit[maxn][maxn],flag;
11: int n,k;
12: int main()
13: {
14: //freopen("test.txt","r",stdin);
15: while(scanf("%d%d",&n,&k)!= EOF)
16: {
17: flag = true;
18: memset(map,0,sizeof(map));
19: memset(ok,0,sizeof(ok));
20: scanf("%s%s",str1,str2);
21: cnt1[0] = 0;
22: cnt2[0] = 0;
23: for(int i=0;i<n;++i)
24: {
25: if(str1[i] == '1')
26: cnt1[i+1] = cnt1[i] + 1;
27: else cnt1[i+1] = cnt1[i] - 1;
28: if(str2[i] == '1')
29: cnt2[i+1] = cnt2[i] + 1;
30: else cnt2[i+1] = cnt2[i] - 1;
31: }
32: for(int i=0;i<=n;++i)
33: for(int j = 0;j<=n;++j)
34: {
35: int temp = cnt1[i] + cnt2[j];
36: if(abs((double)temp) <= k)
37: map[i][j] = 1;
38: }
39: if(!map[n][n])
40: {
41: printf("Poor Alice\n");
42: continue;
43: }
44: ok[n][n] = 1;
45: for(int i = n;i>=0;--i)
46: for(int j = n;j>=0;--j)
47: {
48: if(ok[i][j] && map[i][j-1] && j)
49: ok[i][j-1] = 1;
50: if(ok[i][j] && map[i-1][j] && i)
51: ok[i-1][j] = 1;
52: }
53: if(!(ok[0][1] || ok[1][0]))
54: {printf("Poor Alice\n");continue;}
55: int i=0,j=0;
56: while(i!=n || j!=n)
57: {
58: if(ok[i+1][j])
59: {
60: printf("1");
61: i++;
62: }
63: else if(ok[i][j+1])
64: {
65: printf("2");
66: j++;
67: }
68: }
69: printf("\n");
70:
71: }
72: return 0;
73: }