【题解】 ABC277H Eat them All 思维题

题意:给出九宫格内每个点经过的次数,求从 $(1,1)$ 出发回到 $(1,1)$ 的闭合回路能否实现(并构造)。

官方题解给出了意义不明的欧拉回路,在这里提出一种新做法。

由于每个 $A_{i,j}\geq 1$ ,所以考虑构造“绕一圈、多的次数经过中间点”的方案(如图、稍后证明)

考虑对于能互相到达的点连边,这样图中就有 $12$ 条边,又有 $8$ 个互相独立的方程,所以还差 $4$ 个量待枚举。

考虑枚举哪四个量能够使边的经过次数确定下来。

由于九宫格的中心对称性,我们考虑枚举这四条边被往返的次数。

枚举了红色的这四条边,由于四个角上的点有且仅有两条连接的边,所以蓝色的箭头就都可以确定下来,那么已知边上的点的总经过次数,可以确定绿色箭头。

举个例子:

已知 $A(1,2),A(1,3), a, b$

那么 $x=A(1,2)-a-(A(1,3)-b)$

枚举四个红色箭头并 check 即可

check:每个绿色箭头都大于等于 $0$ ,且和等于 $A(2,2)$

最后输出方案即可。

代码

#include<bits/stdc++.h>
using namespace std;
int A[4][4],s1,s2;
bool first_check()
{
    //中间各个点多余的次数之和就等于中间点的次数,将该式展开即为黑白染色 
    //(A[2][1]-(A[3][1]-b)-a+A[3][2]-(A[3][3]-c)-b+A[2][3]-(A[1][3]-d)-c+A[1][2]-(A[1][1]-a)-d == A[2][2] 
    for(int i=1;i<=3;++i) 
      for(int j=1;j<=3;++j)
        if((i+j)%2) s1+=A[i][j];
        else s2+=A[i][j];
    return (s1==s2);
}
bool second_check(int a,int b,int c,int d)
{return (A[2][1]-(A[3][1]-b)>=a&&A[3][2]-(A[3][3]-c)>=b&&A[2][3]-(A[1][3]-d)>=c&&A[1][2]-(A[1][1]-a)>=d);}
// 角上的点只有两条边,所以经过次数一定为两边之和,边上的点可能与中间的点有连线
void read()
{
    for(int i=1;i<=3;++i)
      for(int j=1;j<=3;++j)
    cin>>A[i][j];
}
char D='D',U='U',L='L',R='R';
void Go(char c) {cout<<c;}
void Lh(int cs,char c1,char c2){for(int i=1;i<=cs;++i)Go(c1),Go(c2);}
void print(int a,int b,int c,int d)
{
//  满足了这些条件后一定中心点的经过次数是固定的,因为黑色点总和等于白色
    // (1,1)->(1,2)[->(2,2)->(1,2)]->(1,3)
    Go(D);
    Lh(a-1,U,D);
    Lh((A[2][1]-(A[3][1]-b))-a,R,L);// 多余的次数就往中间走
    Go(D);
    Lh(A[3][1]-b,U,D); 

    Go(R);
    Lh(b-1,L,R);
    Lh((A[3][2]-(A[3][3]-c))-b,U,D);
    Go(R);
    Lh(A[3][3]-c,L,R);

    Go(U);
    Lh(c-1,D,U);
    Lh((A[2][3]-(A[1][3]-d))-c,L,R);
    Go(U);
    Lh(A[1][3]-d,D,U);

    Go(L);
    Lh(d-1,R,L);
    Lh((A[1][2]-(A[1][1]-a))-d,D,U);
    Go(L);
    Lh(A[1][1]-a,R,L);

}
int main()
{
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    read();
//    extra();
    if(!first_check()) {puts("NO");return 0;}
    for(int down = 1; down <= min(A[1][1],A[2][1]) ; ++down )
      for(int right = 1; right <= min(A[3][1],A[3][2]) ; ++right )
        for(int up = 1; up <= min(A[3][3],A[2][3]) ; ++up )
          for(int left = 1; left <= min(A[1][3],A[1][2]) ; ++left )
            if(second_check( down , right , up ,left ) )  print(down , right , up ,left),exit(0);
    puts("NO");
}
posted @ 2022-11-16 15:07  寂静的海底  阅读(7)  评论(0编辑  收藏  举报  来源