1. 程式人生 > >BZOJ 2466 [中山市選2009]樹(高斯消元)

BZOJ 2466 [中山市選2009]樹(高斯消元)

using bzoj break ble isf 狀態 clas memset c++

【題目鏈接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2466

【題目大意】

  給定一棵樹,每個節點有一盞指示燈和一個按鈕。如果節點的按扭被按了,
  那麽該節點的燈會從熄滅變為點亮(當按之前是熄滅的),或者從點亮到熄滅
  並且該節點的直接鄰居也發生同樣的變化。開始的時候,所有的指示燈都是熄滅的。
  請編程計算最少要按多少次按鈕,才能讓所有節點的指示燈變為點亮狀態。

【題解】

  高斯消元枚舉自由變元回代。

【代碼】

#include <cstdio>
#include <algorithm>
#include <cstring> 
using namespace std;
namespace Gauss{
    const int N=110,MOD=2,INF=1e9;
    int a[N][N],ans[N];
    bool isFreeX[N];
    int inv(int a,int m){return(a==1?1:inv(m%a,m)*(m-m/a)%m);} 
    int getAns(int n,int m,int r){
        int res=0;
        for(int i=r-1;~i;i--){
            for(int j=0;j<m;j++){
                if(!a[i][j])continue;
                ans[j]=a[i][m];
                for(int k=j+1;k<m;k++){
                    ans[j]-=a[i][k]*ans[k];
                    ans[j]%=MOD;
                    if(ans[j]<0)ans[j]+=MOD;
                }
                ans[j]=ans[j]*inv(a[i][j],MOD)%MOD;
                break;
            }
        }
        for(int i=0;i<m;i++)res+=ans[i];
        return res;
    }
    int gauss(int n,int m){
        for(int i=0;i<m;i++)isFreeX[i]=0;
        int r=0,c=0;
        for(;r<n&&c<m;r++,c++){
            int maxR=r;
            for(int i=r+1;i<n;i++)if(abs(a[i][c])>abs(a[maxR][c]))maxR=i;
            if(maxR!=r)swap(a[maxR],a[r]);
            if(!a[r][c]){r--;isFreeX[c]=1;continue;}
            for(int i=r+1;i<n;i++){
                if(a[i][c]){
                    int delta=a[i][c]*inv(a[r][c],MOD);
                    for(int j=c;j<=m;j++){
                        a[i][j]-=delta*a[r][j];
                        a[i][j]%=MOD;
                        if(a[i][j]<0)a[i][j]+=MOD;
                    }
                }
            }
        }
        for(int i=r;i<n;i++)if(a[i][m])return -1;
        return r;
    }
    // 模2枚舉自由變元
    int getMinAns(int n,int m,int r){
        int res=INF,freeX=m-r;
        for(int s=0;s<1<<freeX;s++){
            if(__builtin_popcount(s)>=res)continue;
            int cnt=0;
            for(int j=0;j<m;j++){
                if(isFreeX[j]){
                    ans[j]=s>>cnt&1;
                    ++cnt;
                }
            }res=min(res,getAns(n,m,r));
        }return res;
    }
}
int n,x,y;
int main(){
    while(~scanf("%d",&n),n){
        using namespace Gauss;
        memset(a,0,sizeof(a));
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            a[x-1][y-1]=1;
            a[y-1][x-1]=1;
        }
        for(int i=0;i<n;i++)a[i][i]=a[i][n]=1;
        int r=gauss(n,n);
        printf("%d\n",getMinAns(n,n,r));
    }return 0;
}

BZOJ 2466 [中山市選2009]樹(高斯消元)