zoukankan      html  css  js  c++  java
  • 【bzoj2003】[Hnoi2010]Matrix 矩阵

    首先可以知道,如果已知第一行和第一列的数,那么可以很容易的计算出其余的数。进一步的,如果笔算将每个数的表达式写出可以得出如下结论:

        第i行的第j个数(i>1,j>1)只与(1,1),(i,1),(1,j)有关

    更数学化地说,设输入矩阵为S,定义C如下:

              C(i,j)=0                               i=1或j=1

                 S(i,j)-C(i,j-1)-C(i-1,j)-C(i-1,j-1)    其余情况

    则原矩阵A的第i行的第j个数可按下式确定:

        A(i,j)=F(i)*A(i,1)+F(j)*A(1,j)–F(i)*F(j)*A(1,1)+C(i,j) (i>1且j>1)

        F(x)在x为奇数时为1否则为-1。 

    算法一:

    直接利用该结论,枚举第一行和第一列的每个数,从而求出剩下所有数,并判断是否符合要求。时间复杂度O(NM*P^(N+M-1)),较好的实现可以通过30%的数据。

    算法二:

    考虑P=2的情况,每个格子只有两种取值0/1。首先枚举(1,1),那么第一行某个格子(1,j)与第一列某个格子(i,1)唯一确定了格子(i,j),若(1,j)=x且(i,1)=y时(i,j)的取值不合法,则(1,j)取x和(i,1)取y不能同时发生。

    这是经典的2-SAT模型,虽然使用拓扑排序可以在O(N*M)的时间内求出一个可行解,但无法求出字典序最小解。这里应使用枚举算法,按字典序的优先级判断每一个点,若该点的取值未确定,先尝试放0,看是否产生矛盾,无矛盾则确定为0否则改为1。该算法的时间复杂度为O((N+M)NM),较好的实现可以通过30%的数据。

    算法三:

    结合算法一与算法二可以通过60%的数据。

    算法四:

    对于一般的情况,2-SAT模型并不适用,但可以尝试将算法二稍做修改。即每次从0到P-1枚举取值,找到最小的且不与前面产生矛盾的确定为该格的值。遗憾的是,该算法是错误的,很多时候这样做甚至会丢失可行解!为了避免这种情况,可以使用DFS算法。

    具体来说,依次枚举第一行每个格子的取值,每次枚举后判断第一列每个格子是否均有可行的取值,若没有则退出,否则继续枚举下一个格子。若第一行的格子均已确定,则第一列的每个格子直接取可行取值中最小的即可。

    由于数据保证有解,对于矩阵较大的情况,解很多,限制很多,搜索树的宽度很小,而矩阵较小时搜索树深度很小,均可以较快的得出答案。更进一步的,在矩阵较大时,基本上不会有回溯的情况,即时间复杂度约等于O(NMP^2),可以通过所有测试数据。

    注意:bzoj上没有忽略行末换行和空格,输出有些鬼。。

     

    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
      
    #define N 210
    #define f(a) a[0][0]
      
    int n,m,p;
    int c[N][N],v[N][N],a[N][N],l[N][N],r[N][N];
      
    int check(int x)
    {
        return (x&1) ? -1 : 1;
    }
      
    int dfs(int d)
    {
        if (d>=m)
            return true;
        for (int k=0;k<p;k++)
        {
            bool res(true);
            a[0][d]=k;
            for (int i=1;i<n;i++)
            {
                int r1=(c[i][d]+f(a)*check(i+d+1)+a[0][d]*check(i))*(-check(d));
                int r2=(c[i][d]+f(a)*check(i+d+1)+a[0][d]*check(i)-(p-1))*(-check(d));
                if (r1>r2)
                    swap(r1,r2);
                l[d][i]=max(l[d-1][i],r1);
                r[d][i]=min(r[d-1][i],r2);
                if (l[d][i]>r[d][i])
                {
                    res=false;
                    break;
                }
            }
            if (res)
                if (dfs(d+1))
                    return 1;
        }
        return 0;
    }
      
    int main()
    {
        scanf("%d%d%d",&n,&m,&p);
        for (int i=0;i<n;i++)
            for (int j=0;j<m;j++)
            {
                scanf("%d",&v[i][j]);
                r[j][i]=p-1;
            }
        for (int i=1;i<n;i++)
            for (int j=1;j<m;j++)
                c[i][j]=v[i][j]-(c[i-1][j]+c[i][j-1]+c[i-1][j-1]);
        for (int ii=0;ii<p;ii++)
        {
            f(a)=ii;
            if (dfs(1))
            {
                for (int i=1;i<n;i++)
                    a[i][0]=l[m-1][i];
                for (int j=1;j<n;j++)
                    for (int k=1;k<m;k++)
                        a[j][k]=c[j][k]+check(k)*a[j][0]+check(j)*a[0][k]+check(j+k+1)*f(a);
                break;
            }
        }
        for (int i=0;i<n;i++)
            for (int j=0;j<m;j++)
                printf("%d%s",a[i][j],j+1==m?"
    ":" ");
        return 0;
    }
    

      

  • 相关阅读:
    C#线程同步(1)- 临界区&Lock
    详细解析Java中抽象类和接口的区别
    防止重复提交的几种办法
    网页中实现JSON的编辑与显示
    xcode5 ios7升级后的一系列问题解决
    hadoop-2.0.0-mr1-cdh4.2.0源码编译总结
    hadoop-2.0.0-cdh4.2.1源码编译总结
    cocos2d-iphone加入芒果广告
    hadoop2.0 eclipse 源码编译
    HBase学习笔记
  • 原文地址:https://www.cnblogs.com/yangjiyuan/p/5321082.html
Copyright © 2011-2022 走看看