题目:
题解:
这不是一道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新买了一块长方形的新牧场,这块牧场被划分成M行N列(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]);
}
}