ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

COCI2011/2012 #1 skakac 状态压缩加速DP

2021-08-28 22:02:04  阅读:160  来源: 互联网

标签:skakac int MAXT COCI2011 ++ MAXN DP dp 1000


COCI2011/2012 #1 skakac 状态压缩加速DP

题目链接:在里面找一下


​ 写在前面:原题的空间为 64MB。但本题在校内 OJ 上空间开了 128MB。而本题解介绍的写法在 64MB 的限制下无法通过。若要在 64MB 的空间内实现,需要根号分治。


​ 这题有一个比较显然的暴力,开一个三维数组 \(dp_{k,i,j}\) 表示第 \(k\) 个时刻,\((i,j)\) 这个位置是否能够到达。但是这个暴力时空的复杂度都为 \(O(T\times n^2)\)。我们接受不了。显然,第一维可以滚动掉,这样我们的空间就够了,但是时间还不够。注意到,\(dp\) 数组记录的是可行性,而 \(n\) 的大小很小,只有 \(30\)。我们可以考虑压位,即用一个二进制数来表示一行的可行性状态(bitset 貌似是这么实现的)。此时如果没有限制的话,我们可以很轻松的写出复杂度为 \(O(T\times n)\) 的转移。

​ 代码如下:

for(R int k=1;k<=T;++k)
	{
		g^=1;e=g^1;
		memset(dp[g],0,sizeof dp[g]);
		for(R int i=1;i<=n;++i)
		{
			if(i>1)
			{
				dp[g][i]|=dp[e][i-1]<<2;
				dp[g][i]|=dp[e][i-1]>>2;
			}
			if(i>2)
			{
				dp[g][i]|=dp[e][i-2]<<1;
				dp[g][i]|=dp[e][i-2]>>1;
			}
			if(i<n)
			{
				dp[g][i]|=dp[e][i+1]<<2;
				dp[g][i]|=dp[e][i+1]>>2;
			}
			if(i<n-1)
			{
				dp[g][i]|=dp[e][i+2]<<1;
				dp[g][i]|=dp[e][i+2]>>1;
			}
		}
	}

​ 但是,题目是有限制的,有些格子只有在特定时间的时候才能跳到。所以我们可以再开一个数组 \(can_{i,j}\) 表示第 \(i\) 个时刻第 \(j\) 行格子可行位置的状态。那么 \(dp_{i,j} \& can_{i,j}\) 得到的就是 \(i\) 这个时刻第 \(j\) 行能够到达的状态

​ \(can\) 数组可以通过预处理得到。代码如下:

for(R int i=1;i<=n;++i)
	{
		for(R int j=1;j<=n;++j)
		{
			scanf("%d",&a[i][j]);
			for(R int k=a[i][j];k<=T;k+=a[i][j]) can[k][i]|=1<<(j-1);
		}
	}

​ 但是这样预处理的时间复杂度上界依然是 \(O(T\times n^2)\)。考虑到时间主要是因为 \(a_{i,j}\) 过小而浪费的,我们可以定义一个参数 \(P\),如果 \(a_{i,j}\le P\) ,我们不预处理它们,而是直接记录,然后在转移的时候,枚举 \(1\) 到 \(P\) 计算出格子中的值为在 \([1,P]\) 范围内的能否到达的情况。那么时间复杂度就为 \(O(\dfrac{T}{P+1}+T\times P+T\times n)\)。对于参数 \(P\) 自己算一下,微调一下就好了。

​ 那么预处理和转移的代码就变成如下:

for(R int i=1;i<=n;++i)
	{
		for(R int j=1;j<=n;++j)
		{
			scanf("%d",&a[i][j]);
			if(a[i][j]<=P) d[a[i][j]][i]|=1<<(j-1);
			else for(R int k=a[i][j];k<=T;k+=a[i][j]) can[k][i]|=1<<(j-1);
		}
	}
	int g=0,e=0;
	for(R int k=1;k<=T;++k)
	{
		g^=1;e=g^1;
		int tmp[31]={};
		memset(dp[g],0,sizeof dp[g]);
		for(R int j=1;j<=P;++j) if(k%j==0) for(R int i=1;i<=n;++i) tmp[i]|=d[j][i];
		for(R int i=1;i<=n;++i)
		{
			if(i>1)
			{
				dp[g][i]|=dp[e][i-1]<<2;
				dp[g][i]|=dp[e][i-1]>>2;
			}
			if(i>2)
			{
				dp[g][i]|=dp[e][i-2]<<1;
				dp[g][i]|=dp[e][i-2]>>1;
			}
			if(i<n)
			{
				dp[g][i]|=dp[e][i+1]<<2;
				dp[g][i]|=dp[e][i+1]>>2;
			}
			if(i<n-1)
			{
				dp[g][i]|=dp[e][i+2]<<1;
				dp[g][i]|=dp[e][i+2]>>1;
			}
			dp[g][i]&=(tmp[i]|can[k][i]);
		}
	}

​ 整个代码的空间瓶颈是 \(can\) 数组。所以本文的代码无法在 64MB 空间限制的情况下通过。

全部代码如下:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define R register
using namespace std;
const int MAXN = 1e6+5;
const int P = 7;
int n,T,x,y;
int dp[2][31],a[35][35],can[MAXN][31],d[10][31];
int main()
{
	scanf("%d %d",&n,&T);
	scanf("%d %d",&x,&y);
	dp[0][x]|=1<<(y-1);
	for(R int i=1;i<=n;++i)
	{
		for(R int j=1;j<=n;++j)
		{
			scanf("%d",&a[i][j]);
			if(a[i][j]<=P) d[a[i][j]][i]|=1<<(j-1);
			else for(R int k=a[i][j];k<=T;k+=a[i][j]) can[k][i]|=1<<(j-1);
		}
	}
	int g=0,e=0;
	for(R int k=1;k<=T;++k)
	{
		g^=1;e=g^1;
		int tmp[31]={};
		memset(dp[g],0,sizeof dp[g]);
		for(R int j=1;j<=P;++j) if(k%j==0) for(R int i=1;i<=n;++i) tmp[i]|=d[j][i];
		for(R int i=1;i<=n;++i)
		{
			if(i>1)
			{
				dp[g][i]|=dp[e][i-1]<<2;
				dp[g][i]|=dp[e][i-1]>>2;
			}
			if(i>2)
			{
				dp[g][i]|=dp[e][i-2]<<1;
				dp[g][i]|=dp[e][i-2]>>1;
			}
			if(i<n)
			{
				dp[g][i]|=dp[e][i+1]<<2;
				dp[g][i]|=dp[e][i+1]>>2;
			}
			if(i<n-1)
			{
				dp[g][i]|=dp[e][i+2]<<1;
				dp[g][i]|=dp[e][i+2]>>1;
			}
			dp[g][i]&=(tmp[i]|can[k][i]);
		}
	}
	int ans=0;
	for(R int i=1;i<=n;++i)
		for(R int j=1;j<=n;++j) if(dp[T&1][i]&(1<<(j-1))) ++ans;
	printf("%d\n",ans);
	for(R int i=1;i<=n;++i)
		for(R int j=1;j<=n;++j) if(dp[T&1][i]&(1<<(j-1))) printf("%d %d\n",i,j);
	return 0;
}

​ 关于 64MB 限制的做法,我粗略的看了下官方题解。大致是对于大于 \(1000\) 的 \(a\) 值,我们直接枚举它的倍数,对于小于 \(1000\) 的 \(a\) 值,我们对其进行质因数分解,然后以更小的空间处理出类似本文 \(can\) 数组的数组。

因为懒,就贴份官方代码好了。。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

int dx[] = { -2, -2, -1, -1, 1, 1, 2, 2 };
int dy[] = { -1, 1, -2, 2, -2, 2, -1, 1 };

const int MAXN = 31;
const int MAXT = 1000001;
const int MAXP = 170; //prosti brojevi manji od 1000
const int MAXPOT = 20; //najveca potencija nekog prostog broja u rastavu broja t

int pr[MAXT], f[MAXT], ff[MAXT], fp[MAXT];
int k[MAXN][MAXN], b[MAXN], bb[MAXN], m[MAXN];
int n, T;

int ind[1000], prosti[MAXP], cnt;
int faktor[MAXP][MAXPOT][MAXN]; // matrica F(p na q)
int fnula[MAXP][MAXP][MAXN]; // matrica G(x,y)

vector< int > v[MAXT];

inline void postavi( int &x, int y, int v ) { // postavlja y-ti bit u x na vrijednost v
  if( v == 1 ) x |= (1<<y); else
    x &= ~(1<<y);
}

inline int daj( int x, int y ) { return (x>>y)&1; } // vraca vrijednost y-tog bita u x

void sito( int MAX ) {
// izracunava sve proste brojeve manje od 1000
   for( int i = 2; i*i < MAXT; ++i )
    if( !pr[i] ) {
      for( int j = i+i; j < MAXT; j += i )
        pr[j] = 1, f[j] = i;
      ind[i] = cnt;
      prosti[cnt++] = i;
    }

// izracunava po jedan prosti faktor i njegov eksponent za svaki broj manji od MAXT kako bi mogli brojeve rastavljati na proste faktore
    for( int i = 2; i < MAXT; ++i ) {
        if( f[i] == 0 ) f[i] = i;
        int x = i;
        ff[i] = 1, fp[i] = 0;
        while( x%f[i] == 0 ) x /= f[i], ff[i] *= f[i], fp[i]++;
    }
}

void solveG() { // izracunava funkciju G(i,j)
    for( int i = 0; i < cnt; ++i ) {
        for( int j = 0; j < n; ++j )
            fnula[i][i][j] = faktor[i][0][j];

    for( int j = i+1; j < cnt; ++j )
      for( int k = 0; k < n; ++k )
        fnula[i][j][k] = (fnula[i][j-1][k] & faktor[j][0][k] );
  }
}

int main( void ) {
  scanf( "%d %d", &n, &T );

  int px, py;
  scanf( "%d %d", &px, &py ); --px, --py;

  sito( MAXT );

  for( int i = 0; i < n; ++i )
    for( int j = 0; j < n; ++j ) {
      scanf( "%d", k[i]+j );
      if( k[i][j] >= 1000 ) { // manje od 1000 slobodnih vremena, sve ih stavi rucno
        for( int l = k[i][j]; l <= T; l += k[i][j] )
          v[l].push_back( i*32 + j );
      } else { // period manji do 1000
        int x = k[i][j];
        for( int p = 0; p < cnt; ++p ) {
          int c = 0;
          while( x%prosti[p] == 0 ) x /= prosti[p], c++;
          while( c < MAXPOT ) postavi( faktor[p][c][i], j, 1 ), c++;
        }
      }
    }

  solveG();

  postavi( b[px], py, 1 );
  int ms1 = (1<<(n-1))-1, ms2 = (1<<(n-2))-1;

  for( int t = 0; t < T; ++t ) {
    // b - trenutna matrica
    // bb - sljedeca matrica
    // m - matrica blokiranosti sekunde t+1

    for( int i = 0; i < n; ++i ) {
      if( i-1 >= 0 ) bb[i-1] |= (b[i]>>2)|((b[i]&ms2)<<2);
      if( i-2 >= 0 ) bb[i-2] |= (b[i]>>1)|((b[i]&ms1)<<1);
      if( i+1 < n ) bb[i+1] |= (b[i]>>2)|((b[i]&ms2)<<2);
      if( i+2 < n ) bb[i+2] |= (b[i]>>1)|((b[i]&ms1)<<1);
    }

    for( int i = 0; i < n; ++i ) {
      b[i] = bb[i], bb[i] = 0;
      m[i] = (1<<n)-1;
    }

    int x = t+1, last = cnt-1;
    while( x > 1 && f[x] < 1000 ) {
      int now = ind[ f[x] ];

      if( last >= now+1 ) {
        for( int i = 0; i < n; ++i )
          m[i] &= fnula[now+1][last][i];
      }
      for( int i = 0; i < n; ++i )
        m[i] &= faktor[now][ fp[x] ][i];
      x /= ff[x], last = now-1;
    }
    if( last >= 0 )
      for( int i = 0; i < n; ++i )
        m[i] &= fnula[0][last][i];

    for( int i = 0; i < v[t+1].size(); ++i )
      postavi( m[ v[t+1][i]/32 ], v[t+1][i]%32, 1 );

    for( int i = 0; i < n; ++i )
      b[i] &= m[i];
  }

  int ans = 0;
  for( int i = 0; i < n; ++i )
    for( int j = 0; j < n; ++j )
      if( daj( b[i], j ) ) ans++;

  printf( "%d\n", ans );
  for( int i = 0; i < n; ++i )
    for( int j = 0; j < n; ++j )
      if( daj( b[i], j ) ) printf( "%d %d\n", i+1, j+1 );
  return 0;
}

标签:skakac,int,MAXT,COCI2011,++,MAXN,DP,dp,1000
来源: https://www.cnblogs.com/nightsky05/p/15201287.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有