uva 11464 Even Parity 递推

本文探讨了在解决复杂问题时采用的优化策略,包括贪心算法、规律发现、递推方法等,强调了理解问题本质、确定不变因素、建立递推关系的重要性,并通过实例展示了如何运用这些策略解决实际问题。

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

题目链接:

https://uva.onlinejudge.org/external/114/11464.pdf


首先可以知道每个数字,可以改变或不改变(当然 依题意 ,是0才能够改变)

这样暴力枚举会超时。

那么既然暴力枚举不行,就证明有非暴力的解决方法:

1. 贪心或规律,(直接找到解题策略)认为的找出改变方法,此策略对所有情况均适用。

2.找出部分规律 ,定下解答的一部分 ,然后计算其余可变化的部分,枚举或有效利用各种算法,减少时间复杂度。

两者均比直接暴力时间要少。

对于一个题目,首先有直接求解法,比如说先直接分步,或是先直接分类,然后加以计算。

                             还有间接求解法,比如说我想知道f[n]的值,却不知道怎样直接求出 f[n], 但我知道f[1]的值,也知道f[i]怎么推出f[i+1],那么我可以从f[1]步步递推,

                            直到求出f[n]。

反观自己,过去对于递推的理解实在很狭隘,不知道递推的好处,也不知道怎么用递推:


首先,分析一个问题,一定要充分抓住已知条件,一个题目里面变化的东西有很多,但是也有不变的东西,

 先要确定不变的是什么,(排序规则 ,选取规则 ,时间长短...)

当初想这个问题,就是使劲去找到底什么不变?选择点的方法?还是有什么数量规律?翻来覆去就是弄不出来。

因为,如果能找出定的信息或规律,一定是要有用的,可以利用求出答案的。


第二,

递推找到起点很重要,知道了dp[0]=1(计数DP),才可能推出后面的,而且这个dp[0]=1是个起点,什么不选的情况就是1种。

因为平时递推的起点很显然,没有注意过,而这个题很隐蔽,所以让人想不到递推。


第三,

递推关系很重要,

这个题的条件其实可以转化为递推关系:   假如某个点的上、左、右已经确定了,那么它下个点就确定了。

(这就是递推关系)

证明我们善于要分析出隐蔽的递推关系

这个题也是,看不出有递推关系,压根想不到递推。 (这个递推关系就是个定的,不变的规律


对于此题,充分利用递推关系,确定递推起点,则可得解。

这个题的解法:首先枚举第一行的情况,然后根据第一行,可以推出第二行,之后根据第二行推出第三行,(证明递推的顺序很重要

直到推出第n行。

属于解法2.


综上:对于一个问题,先要找到问题的大致方向,是暴力枚举,还是人为想出特定解法,还是充分利用 已知条件(还不足以求出答案) 再加上高效算法。

          如果想用递推解决问题,那么先要确立递推关系(状态转移方程),找到隐蔽的递推起点,然后决定递推顺序,

(之所以这样,因为很多题目这三者实在太显然,让我们忽略了它们的重要性)。

int a[maxn][maxn];
int b[maxn][maxn];
int n,ans;bool ok;
int kase;
int dir[4][2]={{-1,0},{0,-1},{0,+1},{+1,0} };
int f(int y,int x)
{
    int ans=0;
    for(int i=0;i<3;i++)
    {
        int ty=y+dir[i][0];
        int tx=x+dir[i][1];
        ans+=b[ty][tx];
    }
    return ans;


}
int f2(int y,int x)
{
    int ans=0;
    for(int i=0;i<4;i++)
    {
        int ty=y+dir[i][0];
        int tx=x+dir[i][1];
        ans+=b[ty][tx];
    }
    return ans;


}
int check( )
{
    int cnt=0;
    for(int i=2;i<=n;i++)
    {


        for(int j=1;j<=n;j++)
        {
            int tmp=f(i-1,j);
            if( (tmp%2) !=a[i][j])
            {
                if(a[i][j]==1)  {cnt=-1;return cnt;}


                cnt++;
                b[i][j]=1;
            }
            else
            {
                b[i][j]=a[i][j];
            }


        }




    }
         /*我担心这样推出后,最后一排的没有验证是否符合,
         所以单独拿出来验证一遍,书中没有验证,我还没想清为什么*/
    for(int i=1;i<=n;i++)
    {
            if(f2(n,i)%2)  {cnt=-1;break;}
    }






    return cnt;
}






void dfs(int step,int num)
{


    if(step==n+1)
    {
        int tmp;
        if( ( tmp=check(  ) )!=-1  )
        {
              ans=min(ans,tmp+num);


        }
        return;
    }


  if(!a[1][step])
  {
      b[1][step]=1;
      dfs(step+1,num+1);
  }


    b[1][step]=a[1][step];
    dfs(step+1,num);






}
int main()
{
    int T;kase=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
        }
        memset(b,0,sizeof b);   //这句话掉了就错,虽然我认为不影响,但要注意。
        ans=INF;
         dfs(1,0);


       if(ans==INF) ans=-1;


        printf("Case %d: %d\n",++kase,ans);


    }




    return 0;
}


头文件:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
#include<vector>
#include<map>
#include<sstream>
#include<set>
#include<stack>
#include<utility>
#pragma comment(linker, "/STACK:102400000,102400000")
#define PI 3.1415926535897932384626
#define eps 1e-10
#define sqr(x) ((x)*(x))
#define FOR0(i,n)  for(int i=0 ;i<(n) ;i++)
#define FOR1(i,n)  for(int i=1 ;i<=(n) ;i++)
#define FORD(i,n)  for(int i=(n) ;i>=0 ;i--)
#define  lson   num<<1,le,mid
#define rson    num<<1|1,mid+1,ri
#define MID   int mid=(le+ri)>>1
#define zero(x)((x>0? x:-x)<1e-15)
#define mk    make_pair
#define _f     first
#define _s     second

using namespace std;
const int INF =0x3f3f3f3f;
const int maxn= 20   ;
//const int maxm=    ;
//const int INF=    ;
typedef long long ll;
//const ll inf =1000000000000000;//1e15;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//fin.close();
//fout.close();
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
//by yskysker123




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值