UVA1332 - Kid's Problem

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4078

题解

先列出一个线性方程组(代数系统为模代数系统)
然后开始消元
注意 n n n不一定是素数,因此可能没有逆元
那我消元的时候就不能做除法,只能做乘法
但是做乘法会产生增根,比如以下例子:
x + y = 1 ( m o d    2 ) x+y=1(\mod 2) x+y=1(mod2)
如果我给方程所有系数扩大 2 2 2,得到 2 x + 2 y = 2 ( m o d    2 ) 2x+2y=2(\mod2) 2x+2y=2(mod2)
显然第二个方程相当于 0 x + 0 y = 0 ( m o d    2 ) 0x+0y=0(\mod 2) 0x+0y=0(mod2)
解集扩大了
我扩大的倍数越多,带来的增根就可能越多,增根的问题我可以最后用一步回代来解决
主要是我如何让增根尽量少?
其实就是尽量让扩大的倍数小一点
用lcm肯定是一个很直接的想法,但是实际证明这样会让方程的增根变得很多很多,可能导致超时
问了万能的UOJ群之后得到一个辗转相除的做法qwq
真是神了
详见代码吧,就是用类似于辗转相除的方法把其中一个消为 0 0 0
辗转相除其实和更相减损是同一个东西
直觉上这样比用lcm要更好一些
解的枚举就是爆搜,一个很不错的方法就是倒着搜
消元之后不是得到一个右上三角吗?
我从最后一个元素开始枚举,这样每次消掉一整列,直觉上矛盾会较早出现,也就能更早的剪枝
虽然最后不需要验证增根就能过这道题,但是理论上讲,最好是要回代一下

代码

#include <bits/stdc++.h>
#define maxn 21
#define eps 1e-8
#define linf (1ll<<60)
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
ll n, m, a, b, ans;
ll read(ll x=0)
{
    ll c, f=1;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return f*x;
}
ll val[maxn], N, K;
struct matrix
{
    ll n, m;
    ll a[maxn][maxn], res[maxn];
    matrix(ll n, ll m):n(n),m(m){cl(a);cl(res);}
    ll* operator[](const ll& index){return a[index];}
    void gauss()
    {
        ll i, j, k, r;
        for(i=1;i<=n;i++)
        {
            r=i;
            for(j=r+1;j<=n;j++)if(a[j][i])r=j;
            if(a[r][i]==0)continue;
            for(j=1;j<=n+1;j++)swap(a[i][j],a[r][j]);
            for(j=i+1;j<=n;j++)
            {
                while(a[j][i])
                {
                    ll rate(a[i][i]/a[j][i]), t;
                    for(k=1;k<=n+1;k++)
                    {
                        t=a[j][k];
                        a[j][k]=( (a[i][k] - rate*a[j][k] ) %N + N ) % N;
                        a[i][k]=t;
                    }
                }
            }
        }
    }
    void show()
    {
        for(ll i=1;i<=n;i++)for(ll j=1;j<=n+1;j++)printf("%2lld",a[i][j]), putchar(j==n+1?10:32);
    }
};
void dfs(ll now, matrix &M, matrix &O, ll s)
{
    ll i, j;
    
    if(s>ans)return;
    
    for(i=1;i<=M.n;i++)
    {
        if(M.res[i]==0)
        {
            ll s=0;
            for(j=now+1;j<=M.n;j++)s+=M[i][j]*val[j];
            if(s%N != M[i][M.n+1]%N)return;
        }
    }

    if(now == 0)
    {
        for(i=1;i<=O.n;i++)
        {
            ll s=0;
            for(j=1;j<=O.n;j++)
            {
                s+=O[i][j]*val[j];
            }
            if(s%N != O[i][O.n+1]%N)return;
        }
        ans=min(ans,s);
        return;
    }

    for(i=1;i<=M.n;i++)if(M[i][now])M.res[i]--;
    for(val[now]=0;val[now]<N;val[now]++)
    {
        dfs(now-1,M,O,s+val[now]);
    }
    for(i=1;i<=M.n;i++)if(M[i][now])M.res[i]++;
}
int main()
{
    ll i, j, k, a, b, p, x;
    while(K=read(), N=read())
    {
        matrix T(K,K+1);
        for(i=1;i<=K;i++)T[i][K+1]=N-read()+1;
        for(i=1;i<=K;i++)
        {
            p=read();
            for(j=1;j<=p;j++)
            {
                a=read(), b=read();
                T[a][i]=b;
            }
        }
        matrix Orign(T);
        T.gauss();
        for(i=1;i<=K;i++)
        {
            for(j=1;j<=K;j++)if(T[i][j])T.res[i]++;
        }
        ans=linf;
        dfs(K,T,Orign,0);
        if(ans==linf)printf("No solution\n");
        else printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值