状压dp小练

题目:

我是超链接

题解:

这不是一道dp,而是一个搜索,只是涉及了二进制的用法,这里就介绍一些二进制的操作

如果要获得第i位的数据,判断((data&(1<<i-1))==0),若真,为0,假,为1; (把1移到i-1的位置,用&判断一下)

如果要设置第i位为1,data=(data|(1<<i-1));    (把1移到i-1的位置,加上data)

如果要设置第i位为0,data=(data&(~(1<<i-1)));    (把1移到i-1的位置,取反,&一下)

如果要将第i位取反,data=(data^(1<<i-1));  (把1移到i-1的位置,异或一下)

如果要取出一个数的最后一个1(lowbit):(data&(-data))        (这里利用的是负数取反加1实际上改变的是二进制最低位的1这个性质)

代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int fs[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
char map[205][205];
int tsd[40005][2];
bool vis[205][205][1<<5-1];
int k,ll,r,c;
struct node
{
	int x,y,step,bs;
};
bool check(int key)
{
	int ans=0;
	for (int i=0;i<=5;i++)
      if (key&(1<<i-1)) ans++;
    if (ans>=k) return 1;
    return 0;
}
int bfs(int i,int j)
{
	queue<node>que;
	vis[i][j][0]=1;
	node now;
	now.x=i;now.y=j;now.step=0;now.bs=0;
	que.push(now);
	while (!que.empty())
	{
		now=que.front();que.pop();
		for (int ii=0;ii<4;ii++)
	    {
		    int xx=now.x+fs[ii][0],yy=now.y+fs[ii][1],step=now.step,key=now.bs;
		    if (xx>r || yy>c || xx<=0 || yy<=0) continue;
		    node now1;now1.x=xx;now1.y=yy;now1.step=step+1;now1.bs=key;
		    if (vis[xx][yy][key]) continue;
			if (map[xx][yy]=='#') continue;
			if (map[xx][yy]=='E' && check(key)) return step+1;
		    if (map[xx][yy]>='0' && map[xx][yy]<='9') 
			  vis[xx][yy][key]=1,now1.bs=key|(1<<(map[xx][yy]-'0')),que.push(now1);
			if (map[xx][yy]=='.') vis[xx][yy][key]=1,que.push(now1); 
			if (map[xx][yy]=='$')
			{
				vis[xx][yy][key]=1,que.push(now1); 
				for (int jj=1;jj<=ll;jj++)
				{
					int x1=tsd[jj][0],y1=tsd[jj][1];
					now1.x=x1;now1.y=y1;
					if (!vis[x1][y1][key])vis[x1][y1][key]=1,que.push(now1); 
				}
		    }
	    }
	}
    return -1;
}
int main()
{
	int T,i,j,be,en;
	scanf("%d",&T);
	while (T--)
	{
		int num=0;ll=0;
		memset(vis,0,sizeof(vis));
		scanf("%d%d%d",&r,&c,&k);
		for (i=1;i<=r;i++)
		  for (j=1;j<=c;j++)
		  {
		    cin>>map[i][j];
		  	if (map[i][j]=='S') be=i,en=j,map[i][j]='.';
		  	if (isdigit(map[i][j])) num++;
		  	if (map[i][j]=='$') tsd[++ll][0]=i,tsd[ll][1]=j;
		  }
		if (k>num){printf("oop!\n");continue;}
		int ans=bfs(be,en);
		if (ans==-1){printf("oop!\n");continue;}
	    else printf("%d\n",ans);
	}
}

题目:

我是超链接

译文:

农场主John新买了一块长方形的新牧场,这块牧场被划分成MN(1 M 12; 1 N 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供

他的奶牛们享用。遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有

公共边。John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

题解:

就是入门题目啦

如果贫瘠的草地用1

草地 00100010

我种 11010000

这样&一下就是0,也就是说只要是0就不冲突

可以用预处理操作左右可能的

pl是这一排的贫瘠区

那f[i][j]满足的条件就是这一排允许,上一排序列允许且这排不冲突

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#define Mod 100000000
using namespace std;
int f[15][1<<13];
int num,s[170],n,m,pl[170];
void dfs(int now,int l,int h)
{
	if (now>n)
	{
		s[++num]=h;
		return;
	}
	for (int i=0;i<=1;i++)
	  if (i<=1-l) dfs(now+1,i,h+i*(1<<now>>1));
}
int main()
{
	int id,i,j,k;
	scanf("%d%d",&m,&n);
	for (i=1;i<=m;i++)
	{
		int hh=1;
		for (j=1;j<=n;j++)
	    {
	    scanf("%d",&id);
		if (!id) pl[i]+=hh;
		hh<<=1;
		}
	}
	dfs(1,0,0);
	f[0][1]=1;  
	for (i=1;i<=m;i++)
	  for (j=1;j<=num;j++)
	  {
	  	if (pl[i]&s[j]) continue;
	    for (k=1;k<=num;k++)
	    {
	    	if (pl[i-1]&s[k] || s[k]&s[j]) continue;
	    	f[i][j]=(f[i][j]+f[i-1][k])%Mod;	  
		}
	  }
	int ans=0;
	for (j=1;j<=num;j++)
	  ans=(ans+f[m][j])%Mod;
	printf("%d",ans);  
}

炮兵阵地

上一题升级版


题目:

我是超链接

题解:

其实仔细一想有三种方法铺满某个格子:

1、用k-1行第j列竖着将第k行第j列铺满

2、由第k行第j列被横铺

3、由第k行第j列被竖铺

a

1   1   来默示横放了一个矩形;

b

0

1    来默示和上方一行一路竖着放了一个矩形;

c

1

0    默示和下面一行一路放了一个矩形;

那么每一行的格子我们要知道上一行的情况,第一行没有上一行,所以最开始预处理第一行的所有情况(从0开始)

可以竖着放横着放,一列的最多摆放方式:(1<<m)-1

代码:

#include <cstdio>
#include <cstring>
#define LL long long
using namespace std;
LL dp[12][1<<12];int n,m;
void ycl(int now,int zh)
{
	if (now==m)
	{
		dp[0][zh]=1;
		return;
	}
	if (now+1<=m)
	  ycl(now+1,zh<<1);//竖着摆
	if (now+2<=m)
	  ycl(now+2,zh<<2|3);//横着摆
}
void dfs(int now,int zh,int pre,int bh)
{
	if (now==m)
	{
		dp[bh][zh]+=dp[bh-1][pre];
		return;
	}
	if (now+1<=m)
	{
		dfs(now+1,zh<<1,pre<<1|1,bh);//竖着摆
		dfs(now+1,zh<<1|1,pre<<1,bh);
	}
	if (now+2<=m)
	  dfs(now+2,zh<<2|3,pre<<2|3,bh);
}
int main()
{
	while (~scanf("%d%d",&n,&m))
	{
		if (n==0 || m==0) break;
		if (n*m%2==1){printf("0\n");continue;}
		memset(dp,0,sizeof(dp));
		ycl(0,0);
		for (int i=1;i<n;i++)
		  dfs(0,0,0,i);
		printf("%I64d\n",dp[n-1][(1<<m)-1]);
	}	  
}

题目:愤怒的小鸟

题解:

学习了一下精度的控制。预处理出每条抛物线可以打到的小鸟。枚举出每个状态对应的没覆盖到的小鸟,添加这条抛物线之后转移

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#define D double
using namespace std;
const D eps=1e-9;
struct zhu{D x,y;}zh[20];
D a,b;
int bird[100][100],f[1<<18];
int dcmp(D x)
{
	if (x<=eps && x>-eps) return 0;
	if (x>eps) return 1;
	return -1;
}
void qj(D x1,D y1,D x2,D y2)
{
	a=b=0;
	if (dcmp(x1-x2)==0) return;
	a=(x2*y1-x1*y2)/(x1*x2*(x1-x2));
	b=(x1*x1*y2-x2*x2*y1)/(x1*x2*(x1-x2));
}
bool on(D x,D y)
{
	if (dcmp(x*x*a+b*x-y)==0) return true;
	return false;
}
int main()
{
	int T,n,m,i,j,k;
	scanf("%d",&T);
	while (T--)
	{
		memset(bird,0,sizeof(bird));
		int ans=1;
		scanf("%d%d",&n,&m);
		for (i=1;i<=n;i++)
		  scanf("%lf%lf",&zh[i].x,&zh[i].y),ans<<=1;
		for (i=1;i<=n;i++)
		  for (j=i+1;j<=n;j++)
		  {
		  	qj(zh[i].x,zh[i].y,zh[j].x,zh[j].y);
		  	if (a>=0) continue;
		  	for (k=1;k<=n;k++) 
		  	  if (on(zh[k].x,zh[k].y)) bird[i][j]|=(1<<(k-1));
		  }
		//f[i]表示打掉的猪状态为i时最少使用的小鸟 
		//bird[i][j]哪些猪在这条抛物线上 
		memset(f,0x7f,sizeof(f));
		f[0]=0;
		for (i=0;i<ans;i++)
	      for (j=1;j<=n;j++)
	        if ((i&(1<<(j-1)))==0)
	        {
	        	for (k=j+1;k<=n;k++)
	        	  if ((i&(1<<(k-1)))==0)
	        	    f[i|bird[j][k]]=min(f[i|bird[j][k]],f[i]+1);
	        	f[i|(1<<(j-1))]=min(f[i|(1<<(j-1))],f[i]+1);
	        	break;
			}
	    printf("%d\n",f[ans-1]);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值