1 条题解

  • 0
    @ 2025-5-5 16:18:08
    
    
    #include<bits/stdc++.h>
    using namespace std;
    const int N = 16;
    int ones[1 << N], mp[1 << N];
    int state[N][N]; //state[x][y] 表示,第x行,第y列可以填哪些数字 (0-15)
    char str[N + 2][N + 2];
    int state1[N*N + 1][N][N], state2[N*N + 1][N][N];
    char str1[N*N + 1][N + 2][N + 2];
    int lowbit(int x)
    {
    	return x & -x;
    }
    
    void fill(int x, int y, int c)
    {
    	str[x][y] = 'A' + c;
    	
    	for(int i = 0; i < N; i++)
    	{
    		state[x][i] = state[x][i] & (~(1 << c));
    		state[i][y] = state[i][y] & (~(1 << c));
    	}
    	
    	int nx = x / 4 * 4, ny = y / 4 * 4; //nx ny 表示(x,y)所在十六宫格左上角的下标
    	for(int i = 0; i < 4; i++)
    		for(int j = 0; j < 4; j++)
    			state[nx + i][ny + j] &= ~(1 << c);
    	
    	state[x][y] = 1 << c;
    }
    
    bool dfs(int cnt) 
    {
    	if(cnt == 0) return true; //找到方案
    	
    	
    	int cnt1 = cnt;
    	memcpy(state1[cnt1], state, sizeof state); //将state拷贝一份,便于还原
    	memcpy(str1[cnt1], str, sizeof str);
    	//剪枝1:如果某个空格 不能填任何数字了,就返回, 如果只能填1个数字,就填了
    	for(int i = 0; i < N; i++)
    		for(int j = 0; j < N; j++)
    		{
    			if(str[i][j] == '-')
    			{
    				if(state[i][j] == 0)
    				{
    					memcpy(state, state1[cnt1], sizeof state);
    					memcpy(str, str1[cnt1], sizeof str);
    					return false;
    				}
    				
    				if(ones[state[i][j]] == 1)
    				{
    					fill(i, j, mp[state[i][j]]);
    					cnt--;
    				}
    			}
    		}
    	
    //	剪枝2:对于每一行,如果某个数字不能填,则返回false,如果只能填一个数,就填了
    	for(int i = 0; i < N; i++)
    	{
    		int a = 0, b = (1 << N) - 1; //b表示当前行所有数字都可以填
    		int c = 0;
    		for(int j = 0; j < N; j++)
    		{
    			int s = state[i][j];
    			b &= ~(a & s); //把不能填的数字删掉
    			a |= s; //把所有目前出现的1都并在一个数字里
    			if(str[i][j] != '-') //把所有的填过数字的格子记录下来 
    				c |= state[i][j];
    		}
    		
    		if(a != (1 << N) - 1) //填补了0-15
    		{
    			memcpy(state, state1[cnt1], sizeof state);
    			memcpy(str, str1[cnt1], sizeof str);
    			return false;
    		}
    		
    		for(int j = b; j; j -= lowbit(j))
    		{
    			int s = lowbit(j);
    			if(!(c & s)) 
    			{
    				for(int k = 0; k < N; k++)
    					if(state[i][k] & s)
    					{
    						fill(i, k, mp[s]);
    						cnt --;
    						break;
    					}
    			}
    		}
    	}
    //	
    //	//剪枝3, 对于每一列
    	
    	for(int i = 0; i < N; i++)
    	{
    		int a = 0, b = (1 << N) - 1; //b表示当前行所有数字都可以填
    		int c = 0;
    		for(int j = 0; j < N; j++)
    		{
    			int s = state[j][i];
    			b &= ~(a & s); //把不能填的数字删掉
    			a |= s; //把所有目前出现的1都并在一个数字里
    			if(str[j][i] != '-') //把所有的填过数字的格子记录下来 
    				c |= state[j][i];
    		}
    		
    		if(a != (1 << N) - 1) //填不了0-15
    		{
    			memcpy(state, state1[cnt1], sizeof state);
    			memcpy(str, str1[cnt1], sizeof str);
    			return false;
    		}
    		
    		for(int j = b; j; j -= lowbit(j))
    		{
    			int s = lowbit(j);
    			if(!(c & s)) 
    			{
    				for(int k = 0; k < N; k++)
    					if(state[k][i] & s)
    					{
    						fill(k, i, mp[s]);
    						cnt --;
    						break;
    					}
    			}
    		}
    	}
    //	
    //	//剪枝4 十六宫格
    	for(int i = 0; i < N; i++)
    	{
    		int a = 0, b = (1 << N) - 1; //b表示当前行所有数字都可以填
    		int c = 0;
    		
    		for(int j = 0; j < N; j++) //枚举十六宫格的每个位置
    		{
    			int nx = i / 4 * 4, ny = i % 4 * 4;
    			int dx = j / 4, dy = j % 4;
    			int s = state[nx + dx][ny + dy];
    			b &= ~(a & s); //把不能填的数字删掉
    			a |= s; //把所有目前出现的1都并在一个数字里
    			if(str[nx + dx][ny + dy] != '-') //把所有的填过数字的格子记录下来 
    				c |= state[nx + dx][ny + dy];
    
    		}
    		
    		if(a != (1 << N) - 1) //填不了0-15
    		{
    
    			memcpy(state, state1[cnt1], sizeof state);
    			memcpy(str, str1[cnt1], sizeof str);
    			return false;
    		}
    		
    		for(int j = b; j; j -= lowbit(j))
    		{
    			int s = lowbit(j);
    			if(!(c & s)) 
    			{
    				for(int k = 0; k < N; k++)
    				{
    					int nx = i / 4 * 4, ny = i % 4 * 4;
    					int dx = k / 4, dy = k % 4;
    					if(state[nx + dx][ny + dy] & s)
    					{
    						fill(nx + dx, ny + dy, mp[s]);
    						cnt --;
    						break;
    					}
    				}
    			}
    		}
    		
    	}
    	
    	
    	if(cnt == 0) return true;
    
    	
    	int x, y, s = 100;
    	for(int i = 0; i < N; i++)
    		for(int j = 0; j < N; j++)
    			if(str[i][j] == '-' && ones[state[i][j]] < s)
    			{
    				s = ones[state[i][j]];
    				x = i, y = j;
    			}
    	
    	memcpy(state2[cnt1], state, sizeof state);
    	for(int i = state[x][y]; i; i -= lowbit(i))
    	{
    		memcpy(state, state2[cnt1], sizeof state);
    		fill(x, y, mp[lowbit(i)]);
    		
    		if(dfs(cnt - 1)) return true;
    		
    	}
    	
    	//恢复现场
    	memcpy(state, state1[cnt1], sizeof state);
    	memcpy(str, str1[cnt1], sizeof str);
    	
    	return false;
    }
    
    int main()
    {
    	for(int i = 0; i < N; i++) mp[1 << i] = i;
    	for(int i = 0; i < (1 << N); i++)
    		for(int j = i; j; j -= lowbit(j))
    			ones[i] ++;
    	
    	while(cin >> str[0])
    	{
    		for(int i = 1; i < N; i++) cin >> str[i];
    		
    		for(int i = 0; i < N; i++)
    			for(int j = 0; j < N; j++)
    				state[i][j] = (1 << 16) - 1;
    		
    		int cnt = 0; //空格的个数
    		for(int i = 0; i < N; i++)
    			for(int j = 0; j < N; j++)
    				if(str[i][j] != '-') fill(i, j, str[i][j] - 'A');
    		else cnt ++;
    
    		
    		dfs(cnt);
    		
    		for(int i = 0; i < N; i++) cout << str[i] << endl;
    		cout << endl;
    		
    	}
    	return 0;
    }
    
    
    /*
    枚举每一个空格,枚举空格能填的数字,不断递归
    
    剪枝:
    1.空格能填数字少的优先枚举
    2.如果某个空格,不能填任何数字,直接返回。
    3.对于每一行,每一列、每一个16宫格,如果存在填不了的情况,则直接返回,否则每个数字只能填一次。
    
    ones[i] 表示二进制数i中1的个数  mp[1 << i] = i;  代表1在第i位,数字i能够使用(未被填过)
    */
    
    • 1

    信息

    ID
    103
    时间
    1000ms
    内存
    256MiB
    难度
    9
    标签
    递交数
    12
    已通过
    4
    上传者