K

题目大意:

给定两个长度为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: }
原文地址:https://www.cnblogs.com/bovine/p/2393225.html