#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
//#define INPUT
/**
Problem: poj3740 - Easy Finding
Begin Time:8:30 p.m. 20th/Mar/2012
End Time: 8:42 p.m. 21st/Mar/2012
Last Time: Maybe 4hours-;
Input:
Standard output:
Knowledge point:
DFS+回溯+剪枝
State: WA x 1 , TLE x 1 -> AC
Thought:
--------------------AC后的总结----------------------
1.定义一个数组selectedCol,表示目前选出了几列中有"1".
2.在choose之前,要检查selectedRow[i]是否为true.
如果是true,这行就选过了,就continue
3.在每次choose某行的时候,都在choose函数里进行check
if choose_row[i] == 1 && selectedCol[i] == 1 return false;
if choose_row[i] && !selectedCol[i] => selectedCol[i] = 1;
但是在choose之前记得保存selectedCol的状态,以便return false之前进行回溯。
4.如果choose失败的话,就要检查是否已经找到了全1,如果找到了
isFound = true;
在递归函数里检查isFound = true的话就return true;
5.如果没找到的话,记得回溯。这就要求在递归函数里每次保存selectedCol的状态,失败了进行回溯。
6.如果leftRow == 0 ,那么就check,如果check,return true;,否则return false;
剪枝策略:
每层递归都是从 i = 0 to rowNum 开始选
如果上层递归选了i,那就下层递归从i+1开始选。
因为上层递归从0开始选,选到i的话[0,i]的情况已经由上层递归选完了。
而且行数跟顺序无关
比如说
第一层递归选了1
那么第二,第三……一直到最后一层递归,肯定是把第一次选1的所有情况都遍历了,
第一层递归才能去选2
而且
1 2 3 4 5 6
和
6 5 4 3 2 1
这么选择是没有区别的,所以下一层递归从ind+1开始就可以了
加上这个剪枝,653ms就过了。
更变态的思路:
由于是0,1我们可以按位表示嘛!然后按照规则算,应该会非常快~
递归函数定义:
f(int totalRow,int totalCol,int leftRow,int ind);
totalRow表示总共有多少行
totalCol表示列数
leftRow表示还有几行没选
int ind表示从现在第几行开始选。
--------------------AC前的涂鸦----------------------
其实这道题跟STICK是差不多的,当然,状态压缩是必须的。
每行压缩成一个数字,然后是否有几个数字相加起来正好等于
1111111....
注意,最多可以有10^300,所以这个比较囧!
每一个状态就是按行i是否被选择来决定,
选择了某一行之后,就定义tmp[m]对应的列已经被占领。
那么剩下的就转化为用leftNum - 1行来拼出来其他的列了,如果都能拼出来,那么
return true,否则return false;
定义函数f(rowNum,colNum,leftNum);表示总共有rowNum列,有colNum行,剩leftNum没有选
每次选择的时候定义一个check,看看是否已经选出了每列全有1的行。
还有一个check2,看看当前选的行是否跟以前的行有冲突。
**/
using namespace std;
int matrix[20][310]; ///开大一点总是没错的
bool selectedCol[310];
bool selectedRow[18];
bool isFound;
bool checked(const int colNum)
{
for(int i = 0 ; i < colNum ; i++)
{
if (!selectedCol[i])
return false;
}
return true;
}
bool choose(const int a[310],int colNum)
{
int tmp_state[310];
memset(tmp_state,0,sizeof(tmp_state));
memcpy(tmp_state,selectedCol,sizeof(tmp_state));
for(int i = 0 ; i < colNum ; i++)
{
if(!selectedCol[i] && a[i] )
{
selectedCol[i] = a[i];
}
else
{
if ( selectedCol[i] && a[i] )
{
memcpy(selectedCol,tmp_state,sizeof(tmp_state));
return false;
}
}
}
return true;
}
bool Solve(int rowNum,int colNum,int leftNum,int ind)
{
int tmp_state[310];
memset(tmp_state,0,sizeof(tmp_state));
if( isFound )
return true;
if (leftNum == 0)
{
if ( checked(colNum) )
{
isFound = true;
return true;
}
else
{
return false;
}
}
for(int i = ind ; i < rowNum ; i++)
{
memcpy(tmp_state,selectedCol,sizeof(tmp_state));
if( isFound ) return true;
if ( selectedRow[i] ) continue;
if ( choose(matrix[i],colNum) )
{
if( selectedRow[i] != true && !checked(colNum) )
{
selectedRow[i] = true;
Solve(rowNum,colNum,leftNum - 1,i+1);
}
else
{
if ( checked(colNum) )
{
isFound = true;
return true;
}
}
// memcpy(matrix[i],tmp_state,sizeof(tmp_state));
memcpy(selectedCol,tmp_state,sizeof(selectedCol));
selectedRow[i] = false;
}
// selectedRow[i] = false;
}
return false;
}
int main()
{
int n,m;
#ifdef INPUT
freopen("b:\\acm\\poj3740\\input.txt","r",stdin);
#endif
while(scanf("%d%d",&m,&n) != EOF )
{
isFound = false;
memset(matrix,0,sizeof(matrix));
memset(selectedCol,0,sizeof(selectedCol));
memset(selectedRow,0,sizeof(selectedRow));
for(int i = 0 ; i < m ; i++)
{
for(int j = 0 ; j < n ; j++)
{
scanf("%d",&matrix[i][j]);
}
}
if ( Solve(m,n,m,0) )
{
printf("Yes, I found it\n");
}
else
{
printf("It is impossible\n");
}
}
return 0;
}
【POJ3740】Easy Finding,解题报告+思路+代码
最新推荐文章于 2021-07-31 16:39:16 发布