状压dp几道题

感觉现在只会用比较无脑比较暴力的状压dp,完全没思考,就是枚举所有的状态,等集训结束了搜点要努力想dp方程的题做做。

hdu3681 prison break

大体意思就是给了张地图,走路要耗费能量,有能量池能补充能量,求要走完特定的几个点初始能量的最小值

因为给的点很少所以可以用状压dp,走过的各自可以重复走所以图本身的意义就不大了,就是首先要spfa求一下各点之间的距离

dp[s][i]=max(dp[s-(1<<i)][j]+dis[i][j])

我用了二分找初始能量的最小值,dp的过程很暴力就过了,这部分做的几道题都比较基础,一般都能暴力过。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
#define INF 10000000
typedef struct P
{
	int x,y;
} POINT;
char map[16][16];
int n,cnt,dp[35000][16],cnt_y,m,dir[4][2]={0,1,0,-1,1,0,-1,0},d[16][16],num[16][16];
bool judge (int x,int y)
{
	if(x>=0&&x<n&&y>=0&&y<m&&map[x][y]!='D') return 1;
	return 0;
}
void ini()
{
	memset(num,0,sizeof(num));
	for(int i=0;i<=15;i++)
		for(int j=0;j<=15;j++)
			d[i][j]=INF;
	cnt_y=0;
	cnt=1;
	return;
}
void input()
{
	for(int i=0;i<=n-1;i++)
	{
		scanf("%s",map[i]);
	}
	for(int i=0;i<=n-1;i++)
		for(int j=0;j<=m-1;j++)
			if(map[i][j]=='Y')
			{
				cnt_y++;
				num[i][j]=cnt++;
			}
	for(int i=0;i<=n-1;i++)
		for(int j=0;j<=m-1;j++)
			if(map[i][j]=='G')
			{
				num[i][j]=cnt++;
			}
	for(int i=0;i<=cnt;i++)
		for(int j=0;j<=cnt;j++)
			d[i][j]=INF;
	return;
}
void spfa(int x,int y)
{
	POINT now,next;
	now.x=x;
	now.y=y;
	queue<POINT> Q;
	Q.push(now);
	int a[16][16],v[16][16];
	memset(v,0,sizeof(v));
	for(int i=0;i<=cnt;i++)
		for(int j=0;j<=cnt;j++)
			a[i][j]=INF;
	a[x][y]=0;
	while(!Q.empty())
	{
		now=Q.front();
		Q.pop();
		v[now.x][now.y]=0;
		for(int i=0;i<=3;i++)
		{
			next.x=now.x+dir[i][0];
			next.y=now.y+dir[i][1];
			if(judge(next.x,next.y)&&a[now.x][now.y]+1<=a[next.x][next.y])
			{
				a[next.x][next.y]=a[now.x][now.y]+1;
				if(num[next.x][next.y]||map[next.x][next.y]=='F') d[num[next.x][next.y]][num[x][y]]=a[now.x][now.y]+1;
				if(!v[next.x][next.y])
				{
					Q.push(next);
					v[next.x][next.y]=1;
				}
			}
		}
	}
	return;
}
int cnt_bit(int x)
{
	int ans=0,tmp,j=1;
	while(x)
	{
		if(x&1)
		{
			ans++;
			tmp=j;
		}
		x>>=1;
		j++;
	}
	if(ans==1) return tmp;
	return 0;
}
bool check(int x)
{
	for(int i=0;i<=cnt_y-1;i++)
	{
		if(x&(1<<i)) continue;
		else return 0;
	}
	return 1;
}
int deal(int energy)
{
	memset(dp,-1,sizeof(dp));
	for(int i=1;i<=(1<<cnt)-1;i++)
	{
		int tmp=cnt_bit(i);
		if(tmp)
		{
			if(energy>=d[tmp][0])
			{
				dp[i][tmp-1]=energy-d[tmp][0];
				if(tmp>cnt_y) dp[i][tmp-1]=energy;
			}
			continue;
		}
		int j=0;
		while((1<<j)<=i)
		{
			if((1<<j)&i)
			{
				int k=0;
				while((1<<k)<=i)
				{
					if(k!=j&&(i&(1<<k)))
					{
						if(dp[i-(1<<j)][k]>=d[j+1][k+1])
						{
							dp[i][j]=max(dp[i-(1<<j)][k]-d[j+1][k+1],dp[i][j]);
							if(j>=cnt_y) dp[i][j]=energy;
						}
					}
					k++;
				}
				if(check(i)&&dp[i][j]>=0) 
				{
					int a=1;
					return 1;
				}
			}
			j++;
		}
	}
	return 0;
}
int binary_search(int l,int r)
{
	if(r-l<=1)
	{
		if(deal(l)) return l;
		else return r;
	}
	int m=(l+r)/2;
	if(deal(m)) return binary_search(l,m);
	return binary_search(m+1,r);
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF&&(n!=0||m!=0))
	{
		ini();
		input();
		for(int i=0;i<=n-1;i++)
			for(int j=0;j<=m-1;j++)
				if(map[i][j]=='F'||num[i][j]) spfa(i,j);
		int ans=binary_search(1,10000);
		if(deal(ans))
			printf("%d\n",ans);
		else printf("-1\n");
	}
	return 0;
}

hdu1789 doing homework

题意就是许多作业,给你完成各项作业需要的时间和交作业的时限,晚交一天减一分,求减的最小分数。

一看见这种题就想用贪心,同样N给的比较小,又是状压DP的专题,于是就没仔细考虑贪心的问题。。

dp[s][i]=max(0,dp[s-(1<<i)][j]+time[i]-end[i])

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
#define INF 100000000
int n,parent[35000],dp[35000],t[16],e[16],tmp1[16],tmp2[16];
string subject[20];
void ini()
{
	memset(parent,-1,sizeof(parent));
	for(int i=0;i<=(1<<n)-1;i++)
		dp[i]=INF;
	return;
}
void input()
{
	scanf("%d",&n);
	for(int i=0;i<=n-1;i++)
	{
		cin>>subject[i]>>e[i]>>t[i];
	}
	for(int i=0;i<=(1<<n)-1;i++)
		dp[i]=INF;
	dp[0]=0;
	return;
}
int time(int x)
{
	int i=0,ans=0;
	while(x)
	{
		if(x&1) ans+=t[i];
		x>>=1;
		i++;
	}
	return ans;
}
void exchange(int sit,int a[],int &cnt)
{
	if(sit==0) return;
	exchange(sit-(1<<parent[sit]),a,cnt);
	a[cnt++]=parent[sit];
	return;
}
bool check(int sit1,int sit2)
{
	int cnt1=0,cnt2=0;
	exchange(sit1,tmp1,cnt1);
	exchange(sit2,tmp2,cnt2);
	int i;
	for(i=0;i<cnt1&&i<cnt2;i++)
	{
		if(tmp1[i]>tmp2[i]) return 1;
	}
	if(i!=cnt2&&i==cnt1) return 1;
	return 0;
}
void deal()
{
	for(int i=1;i<=(1<<n)-1;i++)
	{
		int j=0;
		while((1<<j)<=i)
		{
			if(i&(1<<j))
			{
				int a=max(0,time(i-(1<<j))+t[j]-e[j])+dp[i-(1<<j)];
				if(dp[i]>a)
				{
					dp[i]=a;
					parent[i]=j;
				}
				else if(dp[i]==a)
				{
					if(check(i-(1<<parent[i]),i-(1<<j))) parent[i]=j;
				}
			}
			j++;
		}
	}
	return;
}
void output()
{
	int x=(1<<n)-1;
	cout<<dp[x]<<endl;
	int tmp[16],j=n-1;
	while(x)
	{
		int v=parent[x];
		tmp[j--]=v;
		x-=(1<<v);
	}
	for(int i=0;i<=n-1;i++)
		cout<<subject[tmp[i]]<<endl;
	return;
}

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		ini();
		input();
		deal();
		output();
	}
	return 0;
}
hdu 4856 Tunnels

题意:一张地图,上面有许多通道,给你各个通道的起点和终点,问要走所有的通道,在通道之间走的距离的最小值是多少。

跟A题几乎完全一样,SPFA求出各个通道的起点到其他各个通道终点的最短距离,然后用状压dp暴力解决。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 100000000
char map[25][25];
int dir[4][2]={0,1,0,-1,1,0,-1,0},n,m,dis[20][20],dp[35000][20];
typedef struct point{
	int x,y;
}POINT;
typedef struct tunnel{
	POINT s,e;
}TUNNEL;
TUNNEL t[20];
bool check(POINT a)
{
	int x=a.x,y=a.y;
	if(x>=0&&x<n&&y>=0&&y<n&&map[x][y]!='#') return 1;
	return 0;
}
void spfa(POINT x,int num)
{
	POINT now,next;
	queue<POINT> Q;
	Q.push(x);
	bool v[20][20];
	int d[20][20];
	for(int i=0;i<=n-1;i++)
		for(int j=0;j<=n-1;j++)
			d[i][j]=INF;
	d[x.x][x.y]=0;
	memset(v,0,sizeof(v));
	while(!Q.empty())
	{
		now=Q.front();
		Q.pop();
		v[now.x][now.y]=0;
		for(int i=0;i<=3;i++)
		{
			next.x=now.x+dir[i][0];
			next.y=now.y+dir[i][1];
			if(check(next)&&d[next.x][next.y]>d[now.x][now.y]+1)
			{
				d[next.x][next.y]=d[now.x][now.y]+1;
				if(!v[next.x][next.y]) 
				{
					Q.push(next);
					v[next.x][next.y]=1;
				}
			}
		}
	}
	for(int i=0;i<=m-1;i++)
	{
		TUNNEL tmp=t[i];
		if(num!=i)
			dis[num][i]=d[tmp.s.x][tmp.s.y];//从num到i,num是出口
		else dis[num][i]=0;
	}
	return;
}
void initial()
{
	for(int i=0;i<=m-1;i++)
		for(int j=0;j<=m-1;j++)
			if(i!=j) dis[i][j]=INF;
	for(int i=0;i<=(1<<m)-1;i++)
		for(int j=0;j<=m-1;j++)
			dp[i][j]=INF;
	return;
}
void input()
{
	for(int i=0;i<=n-1;i++)
		scanf("%s",map[i]);
	for(int i=0;i<=m-1;i++)
	{
		POINT S,E;
		scanf("%d%d%d%d",&S.x,&S.y,&E.x,&E.y);
		S.x--;
		S.y--;
		E.x--;
		E.y--;
		t[i].s=S;
		t[i].e=E;
	}
	return;
}
int cnt_bit(int x)
{
	int ret=0,tmp,j=0;
	while(x)
	{
		j++;
		if(x&1)
		{
			ret++;
			tmp=j;
		}
		x>>=1;
	}
	if(ret==1) return tmp;
	return 0;
}
void deal()
{
	for(int i=1;i<=(1<<m)-1;i++)
	{
		int j=0,tmp=cnt_bit(i);
		if(tmp) 
		{
			dp[i][tmp-1]=0;
			continue;
		}
		while((1<<j)<=i)
		{
			if(i&(1<<j))
			{
				int k=0;
				while((1<<k)<=i)
				{
					if(j!=k&&(i&(1<<k)))
					{
						dp[i][j]=min(dp[i-(1<<j)][k]+dis[k][j],dp[i][j]);
					}
					k++;
				}
			}
			j++;
		}
	}
	return;
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		initial();
		input();
		for(int i=0;i<=m-1;i++)
			spfa(t[i].e,i);
		deal();
		int ans=INF;
		for(int i=0;i<=m-1;i++)
		{
			ans=min(ans,dp[(1<<m)-1][i]);
		}
		if(ans==INF) ans=-1;
		printf("%d\n",ans);
	}
	return 0;
}

Hdu 4568  Hunter 同上= = 函数里一个m写成n调了一个上午。。什么时候改了这个毛病- -

poj 1185 炮兵阵地

超级经典的题,总之地图上是山的地方不能放,横着竖着两格之内放了阵地的不能放,问最多能放多少个阵地

因为求一行的时候要知道前两行的状态,所以dp中要一次存这一行和上一行的状态,当然还要存行数,第一次做的时候就被这个状态坑了,如果真把所有状态都存起来肯定要爆内存的,纠结好一会儿没敢写,上网搜了以后才知道因为阵地之间至少隔两个格,那么满足条件的状态其实是确定的,先把这些状态打表就好了,至于为什么只有60种,等集训完了再证一下好了。

dp[row][s][pre]=max(dp[row-1][pre][pre_pre]+cnt_bit(s));

打表的时候因为用得递归所以大数放在前面了,下面递归的时候想当然地以为第0个状态就是0,又是坑了一上午,蠢哭,最后还是拿出以前的代码对拍才过的。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int map[105],n,m,dp[103][100][100],s[100],amo,v[103][100][100];
void input()
{
	char tmp[15];
	for(int i=0;i<=n-1;i++)
	{
		scanf("%s",tmp);
		for(int j=0;j<=m-1;j++)
		{
			map[i]<<=1;
			if(tmp[j]=='P')
				map[i]+=0;
			else map[i]+=1;
		}
	}
	return;
}
int bit_cnt(int x)
{
	int cnt=0;
	while(x)
	{
		if(x&1) cnt++;
		x>>=1;
	}
	return cnt;
}
bool check(int sit,int ground)
{
	if(sit&ground) return 0;
	return 1;
}
void pre(int sit,int bit)
{
	if(bit==m)
	{
		s[amo++]=sit;
		return;
	}
	if(!(sit&2)&&!(sit&1)) pre(sit<<1|1,bit+1);
	pre(sit<<1,bit+1);
	return;
}
void dfs(int row,int sit1,int sit2)
{
	if(row==n-1)
	{
		dp[row][sit1][sit2]=bit_cnt(s[sit1]);
		return;
	}
	for(int i=0;i<=amo-1;i++)
	{
		if(check(s[i],map[row+1])&&check(s[i],s[sit1])&&check(s[i],s[sit2]))
		{//i=3和4
			if(!v[row+1][i][sit1])
			{
				v[row+1][i][sit1]=1;
				dfs(row+1,i,sit1);
			}
			dp[row][sit1][sit2]=max(dp[row+1][i][sit1]+bit_cnt(s[sit1]),dp[row][sit1][sit2]);
		}
	}
	return;
}
void init()
{
	memset(dp,0,sizeof(dp));
	amo=0;
	memset(map,0,sizeof(map));
	memset(v,0,sizeof(v));
	return;
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		init();
		pre(0,0);
		input();
		for(int i=0;i<=amo-1;i++)
		{
			if(check(s[i],map[0]))
			{
				dfs(0,i,amo-1);
			}
		}
		int ans=0;
		for(int i=0;i<=amo-1;i++)
				ans=max(ans,dp[0][i][amo-1]);
		printf("%d\n",ans);
	}
	return 0;
}
poj 2411 Mondriaan's Dream

铺砖的题,1*2的砖,问铺满给定区域有多少种铺法,这题也是学长当时讲的例题,如果自己想的话真是无从下手。

把竖着放的砖的上半部分计为1,即1表示下面必须为半块砖。

然后枚举每一行的每一个状态,暴力求解。

一开始就想在一个递归里面解决整个问题,后来发现我的做法是在一行的状态的值求完之前就去求下面的状态,导致有最后的值有加重了的现象,然后脑子一抽干脆每次递归出一个状态都在这个状态上+1,成了个赤裸裸的DFS,但是还没等着TLE,样例就没过,然后没耐心了,其实也想到要先求完上一行的所有状态,但是当时总想着递归解决,还想要递归套递归,实在不好处理,翻出以前的代码恍然大悟。。这尼玛就是两层循环加一个递归= =

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
__int64 h,w,dp[12][2500];
void dfs(__int64 father,__int64 sit,__int64 bit,__int64 row)
{
	if(bit==w)
	{
		dp[row][sit]+=dp[row-1][father];
		return;
	}
	if((father&(1<<(w-bit-1))))	dfs(father,sit<<1,bit+1,row);
	else
	{
		if(bit<=w-2&&!(father&(1<<(w-bit-2)))) dfs(father,sit<<2,bit+2,row); 
		dfs(father,sit<<1|1,bit+1,row);
	}
	return;
}
int main()
{
	while(scanf("%I64d%I64d",&h,&w)!=EOF&&(h!=0||w!=0))
	{
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		for(__int64 i=1;i<=h;i++)
			for(__int64 j=0;j<=(1<<w)-1;j++)
				if(dp[i-1][j]!=0) dfs(j,0,0,i);
		printf("%I64d\n",dp[h][0]);
	}
	return 0;
}
果然不冷静的时候什么都干不了= =

还剩一个D题,总是懒得思考想暴力解出来,T了N次,还是得用脑想吧- -做出来再贴

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值