雜題 翻硬幣

問題 A: 翻硬幣
時間限制: 1 Sec 內存限制: 128 MB
題目描述
有一個n行n列的棋盤,每個格子上都有一個硬幣,且n爲偶數。每個硬幣要麼是正面朝上,要麼是反面朝上。每次操作你可以選定一個格子(x,y),然後將第x行和第y列的所有硬幣都翻面。求將所有硬幣都變成同一個面最少需要的操作數。
輸入
第一行包含一個正整數n。
接下來n行,每行包含一個長度爲n的01字符串,表示棋盤上硬幣的狀態。
輸出
僅包含一行,爲最少需要的操作數。
樣例輸入
4
0101
1000
0010
0101
樣例輸出
2
提示
【樣例說明】
對(2,3)和(3,1)進行操作,最後全變成1。
【數據規模】
對於100%的數據,n ≤ 1,000。

第一反應是高斯消元解異或方程組。然後看到了數據範圍。。。(1000^2)^3的時間效率。QAQ
仔細想想,設a[i][j]是(i,j)的初始狀態,h[i][j]爲是否操作,並且設最後全變成0.那一個點最後是否要操作,取決於和他同一行同一列所有點是否要操作。h[i][j]=h[i][sigma]^h[sigma][j]^a[i][j]。其實雖然其他的h並不知道,但是我們考慮把它們都帶進去,然後就搞出來了一大堆同一行同一列。。。然後,它們互相抵消了。就剩下了,同一行一列的初狀態的異或和^a[i][j],O(N^2)
統計出讓其全變成0的步數,如果最優是全變成1,那麼所有的點是否操作就變得完全相反了。那就和n^2-ans取個min就行了。

#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define N 1005
using namespace std;
char s[N];int n,a[N][N],h[N],z[N],ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=n;j++)
            a[i][j]=s[j]-'0';
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            h[i]^=a[i][j],z[j]^=a[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            int k=h[i]^z[j]^a[i][j];
            ans+=k;
        }
    printf("%d\n",min(n*n-ans,ans));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章