POJ 1166 枚举或者高斯消元

本文介绍了一种通过高斯消元法解决9个钟表同步问题的方法,并提供了一个高效的算法实现,同时也给出了暴力枚举法作为对比。文章详细解释了如何设置方程组并求解最小的操作次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给出9个钟表的状态,给出九种操作,问最少要操作几次能把所有的钟表调回12点。

思路:

对于9个钟表分别列方程,然后高斯消元即可。

然后用每个未知量表示是否进行此操作。所以取值是1或者0

接下来就是枚举每个未知量得系数使得每个等式都成立。

等式的右边为还需要操作的次数。


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define LL long long
#define bug puts("************")
using namespace std;
const int N=110;
int mat[N][N];
char s[10][10]={"ABDE","ABC","BCEF","ADG","BDEFH","CFI","DEGH","GHI","EFHI"};
int a[10];
inline int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
inline int lcm(int a,int b){
    return a/gcd(a,b)*b;
}
int inv(int a,int m){
    if(a==1) return 1;
    return inv(m%a,m)*(m-m/a)%m;
}

void guess(int n,int m){
    int i=0,j=0,r;
    while(i<n&&j<m){
        int r=i;
        for(int k=i;k<n;k++){
            if(mat[k][j]){           ///不可以用abs(mat[k][j])>abs(mat[r][j]) 不知道为啥
                r=k;break;
            }
        }
        if(mat[r][j]==0){
            j++;
            continue;
        }
        if(r!=i){
            for(int k=0;k<=m;k++)swap(mat[r][k],mat[i][k]);
        }
        for(int k=i+1;k<n;k++){
            if(mat[k][j]){
                int LCM=lcm(mat[k][j],mat[i][j]);
                int y=LCM/mat[k][j];
                int x=LCM/mat[i][j];
                for(int g=0;g<=m;g++){
                    mat[k][g]=mat[k][g]*y-mat[i][g]*x;
                    mat[k][g]=(mat[k][g]%4+4)%4;
                }

            }
        }
        i++;
        j++;
    }
//bug;
//        for(int i=0;i<n;i++){  
//        for(int j=0;j<m;j++){
//            cout<<mat[i][j]<<" ";
//        }cout<<endl;
//    }

    int sum=0;
    for(int i=n-1;i>=0;i--){            ///计算每个未知数的系数
        int tmp=mat[i][m];
        for(int j=i+1;j<m;j++){
            if(mat[i][j]) tmp-=mat[i][j]*a[j];
        }
        tmp=(tmp%4+4)%4;
        a[i]=(tmp*inv(mat[i][i],4))%4;      ///求出a[i]的值,等同下方的等式
//        for(a[i]=0;a[i]<=3;a[i]++)             ///
//            if((a[i]*mat[i][i]%4+4)%4==tmp)break;
//        a[i]%=4;
        sum+=a[i];
    }
    for(int i=0;i<m;i++){
        while(a[i]){          ///按从小到大的顺序
            printf("%d",i+1);
            a[i]--;
            sum--;
            printf(sum>0?" ":"\n");
        }
    }
}
int main(){
    for(int i=0;i<9;i++){
        int len=strlen(s[i]);
        for(int j=0;j<len;j++){
            mat[s[i][j]-'A'][i]=1;   ///第一维表示第几个方程,第二维表示第几个未知数,
                                     ///未知数表示取或者不取第i个操作
        }
    }
    for(int i=0;i<9;i++){
        scanf("%d",&mat[i][9]);
        mat[i][9]=(4-mat[i][9])%4;   ///代表还需要操作的次数
    }
    guess(9,9);
    return 0;
}



暴力枚举法:

#include <string.h>
#include <stdio.h>   ///other
int main()
{
      int i,a[10],b[10],c[10];
      for(i=1;i<=9;i++)
      scanf("%d",&a[i]);
      for(b[1]=0;b[1]<=3;b[1]++)
      for(b[2]=0;b[2]<=3;b[2]++)
      for(b[3]=0;b[3]<=3;b[3]++)
      for(b[4]=0;b[4]<=3;b[4]++)
      for(b[5]=0;b[5]<=3;b[5]++)
      for(b[6]=0;b[6]<=3;b[6]++)
      for(b[7]=0;b[7]<=3;b[7]++)
      for(b[8]=0;b[8]<=3;b[8]++)
      for(b[9]=0;b[9]<=3;b[9]++)
      {
            c[1]=(a[1]+b[1]+b[2]+b[4])%4;
            c[2]=(a[2]+b[1]+b[2]+b[3]+b[5])%4;
            c[3]=(a[3]+b[2]+b[3]+b[6])%4;
            c[4]=(a[4]+b[1]+b[4]+b[5]+b[7])%4;
            c[5]=(a[5]+b[1]+b[3]+b[5]+b[7]+b[9])%4;
            c[6]=(a[6]+b[3]+b[5]+b[6]+b[9])%4;
            c[7]=(a[7]+b[4]+b[7]+b[8])%4;
            c[8]=(a[8]+b[5]+b[7]+b[8]+b[9])%4;
            c[9]=(a[9]+b[6]+b[8]+b[9])%4;
            if(c[1]+c[2]+c[3]+c[4]+c[5]+c[6]+c[7]+c[8]+c[9]==0)
            {
                    for(i=0;i<b[1];i++) printf("1 ");
                    for(i=0;i<b[2];i++) printf("2 ");
                    for(i=0;i<b[3];i++) printf("3 ");
                    for(i=0;i<b[4];i++) printf("4 ");
                    for(i=0;i<b[5];i++) printf("5 ");
                    for(i=0;i<b[6];i++) printf("6 ");
                    for(i=0;i<b[7];i++) printf("7 ");
                    for(i=0;i<b[8];i++) printf("8 ");
                    for(i=0;i<b[9];i++) printf("9 ");
                    printf("\n");
                    return 0;
            }
      }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值