1. 程式人生 > >poj-3279 poj-1753(二進位制列舉)

poj-3279 poj-1753(二進位制列舉)

題目連結:http://poj.org/problem?id=3279

題目大意:

  有一個m*n的棋盤(1 ≤ M ≤ 15; 1 ≤ N ≤ 15),每個格子有兩面分別是0或1,每次可以對一個格子做一次翻轉操作,將被操作的格子和與其相鄰的周圍4個格子都會進行翻轉。問做少做多少次翻轉可以將所有格子翻轉成0,輸出翻轉方案(每個棋子的翻轉次數)。沒有方案時輸出“IMPOSSIBLE”。

Sample Input

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

Sample Output

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0

解題思路:

  首先需要明確每個格子只有兩種情況,就是要麼翻和要麼不翻,因為翻奇數次都與翻1次,而翻偶數次等於沒翻。再來看一下資料範圍n和m最大都為15,範圍不是很大,但是全部列舉的話應該也會超時,所以我們要找到一個方案減少列舉量,我們發現如果一行的狀態已經確定了,那麼它接下來的是不是都確定了。下一行都要保證上一行全為0就可以了,最後只需要判斷一下最後一行是否都為0就可以了,而第一行有n個棋子,它的狀態也就是有2^n種。我們可以用一個含有16位的二進位制數來表示它的狀態,如果第i位為1表示該格子需要翻轉,如果為0,則表示不翻轉,翻轉完第一行,僅接著翻第2行,每行的狀態由其上一行的狀態所確定。這樣只要判斷最後一行是否可以全部為0即可,如果為0,判斷是否比答案更小,如果更小,則對答案進行更新。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#include<set>
#include<cmath>
#include<list>
#include<deque>
#include<cstdlib>
#include<bitset>
#include<stack>
#include
<map> #include<queue> using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const double PI=acos(-1.0); const double eps=1e-6; const ll mod=1e9+7; ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} int n,m,mp[20][20],tmp[20][20],cnt[20][20],ans[20][20],res; void flip(int x,int y) //翻轉,用異或符操作 { tmp[x][y]
^=1; tmp[x+1][y]^=1; tmp[x-1][y]^=1; tmp[x][y+1]^=1; tmp[x][y-1]^=1; } int main() { ios_base::sync_with_stdio(false); cin.tie(0); cin>>m>>n; for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { cin>>mp[i][j]; } } res=INF; //初始答案為無窮大 for(int s=0;s<(1<<n);s++) //列舉第一行的所有狀態 { int tot=0; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { tmp[i][j]=mp[i][j]; } } for(int i=0;i<n;i++) { if(((s>>i)&1)) //如果第i+1位為1,則對其進行翻轉 { tot++; cnt[1][i+1]=1; flip(1,i+1); } } for(int i=2;i<=m;i++) //翻轉第2至m行 { for(int j=1;j<=n;j++) { if(tmp[i-1][j]) { tot++; cnt[i][j]=1; flip(i,j); } } } int flag=1; for(int i=1;i<=n;i++) //判斷是否合法 { if(tmp[m][i]!=0) { flag=0; break; } } if(flag&&tot<res) //合法且比當前答案小對答案進行更新 { res=tot; for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { ans[i][j]=cnt[i][j]; } } } } if(res==INF) //不能實現 { cout<<"IMPOSSIBLE"<<endl; return 0; } for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { if(j==1) cout<<ans[i][j]; else cout<<" "<<ans[i][j]; } cout<<endl; } return 0; }

 

 

題目連結:http://poj.org/problem?id=1753

題目大意:

  給你一個4*4的棋盤,上面擺了16個棋子,每個棋子有兩面,一面是黑色一面是白色,給出棋盤的初始狀態,每次可以翻一個棋子,而且每次翻的時候連著四周的棋子一起翻,問最少幾次操作可以使棋盤全變為白棋或者黑棋。

思路:

  首先可以用DFS做,資料範圍較小,最多翻16個棋子(每個棋子要麼翻要麼不翻,再翻就和原來一樣了),列舉翻的棋子數,然後用DFS搜尋翻該棋子數的所有情況,然後進行判斷是否全為黑或全為白,就可以找出答案。

  當然也可以用二進位制列舉的方法,總共16個棋子,對應2^16種狀態,直接暴力列舉全部列舉全部情況,保留答案的最小值,與上面那題類似。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<string>
#include<set>
#include<cmath>
#include<list>
#include<deque>
#include<cstdlib>
#include<bitset>
#include<stack>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
int mp[5][5],tmp[5][5],ans;
void flip(int x,int y)  //翻轉
{
    tmp[x][y]^=1;
    tmp[x+1][y]^=1;
    tmp[x-1][y]^=1;
    tmp[x][y+1]^=1;
    tmp[x][y-1]^=1;
}
int main()
{
    ios_base::sync_with_stdio(false); cin.tie(0);
    for(int i=1;i<=4;i++)
    {
        for(int j=1;j<=4;j++)
        {
            char c;
            cin>>c;
            if(c=='w') mp[i][j]=0;
            else mp[i][j]=1;
        }
    }
    ans=17;
    for(int s=0;s<(1<<16);s++)  //列舉所有情況
    {
        int tot=0;
        for(int i=1;i<=4;i++)
        {
            for(int j=1;j<=4;j++)
                tmp[i][j]=mp[i][j];
        }
        for(int i=0;i<16;i++)
        {
            if((s>>i)&1)
            {
                int x=(i+1)/4+1;  //判斷第i+1棋子的座標,然後對其翻轉
                int y=(i+1)%4;
                if(y==0){x--;y=4;}
                tot++;
                flip(x,y);
            }
        }
        int flag=1;
        for(int i=1;i<=4;i++)  //判斷是否合法
        {
            for(int j=1;j<=4;j++)
            {
                if(tmp[i][j]!=tmp[1][1])
                {
                    flag=0;
                    break;
                }
            }
            if(flag==0) break;
        }
        if(flag&&tot<ans)  //合法更新答案
            ans=tot;
    }
    if(ans==17)
        cout<<"Impossible"<<endl;
    else
        cout<<ans<<endl;
    return 0;
}