状态压缩dp学习小记part2

继续学习状态压缩的相关知识。

本来准备继续按照上篇博文里提到的那篇论文继续学习,但被矩形完全覆盖虐了回来,决定先做些其他的题增进理解之后再回来做。


Zoj 3471 Most Powerful

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3471

题意:不超过10种原子,两两之间相互碰撞可以产生一定的能量,如a碰b,那么b原子就消失,自身不能碰自身,问最后所能得到的最大能量。

#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))

int a[11][11];
int dp[1<<10];

int main ()
{
	int n,i,j;
	while (scanf("%d",&n),n)
	{
		for (i=0;i<n;i++)
			for (j=0;j<n;j++)
				scanf("%d",&a[i][j]);
		memset(dp,0,sizeof(dp));

		for (int s=(1<<n)-1;s>=0;s--)  //初始时全部存在
			for (i=0;i<n;i++)
				if (s&(1<<i))
					for (j=0;j<n;j++)
					{
						if (i==j)
							continue;
						if (s&(1<<j))
						{
							int now=s-(1<<j);  //原子j被i消耗
							dp[now]=max(dp[now],dp[s]+a[i][j]);
						}
					}
		int ans=0;
		for (i=0;i<(1<<n);i++)
			ans=max(ans,dp[i]);
		printf("%d\n",ans);
	}
	return 0;
}


Hdu 4539 郑厂长系列故事——排兵布阵

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4539

思路:和炮兵阵地那个题差不多,使用了滚动数组

要是做腾讯比赛的时候我就学过状态压缩dp该多好……

#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))
 
int n,m;
int top,num[173];
int stk[173],can[103];
int dp[2][173][173];

bool Ok (int x)
{
	if (x&(x<<2))
		return false;
	return true;
}

//计算一个整型数x的二进制中1的个数
int Cal (int x)
{	
	int cnt=0;
	while (x)
	{
		cnt++;
		x&=(x-1);
	}
	return cnt;
}

//找到所有可能的合法状态,170种左右
void Init ()
{
	top=0;
	int total=1<<m;
	for (int i=0;i<total;i++)
		if (Ok(i))
		{
			stk[++top]=i;
			num[top]=Cal(stk[top]);
		}
	memset(dp,0,sizeof(dp));
	memset(can,0,sizeof(can));
}

int main ()
{
#ifdef ONLINE_JUDGE
#else
	freopen("read.txt","r",stdin);
#endif
	while (~scanf("%d%d",&n,&m))
	{
		Init ();
		int i,j,k,t,ans=0;
		for (i=1;i<=n;i++)
		{  
			int temp=0,x;
			for (j=1;j<=m;j++)
			{
				scanf("%d",&x);
				temp=temp*2+x;
			}
			can[i]=temp;   //能放的地方
		}  

		for (i=1;i<=top;i++)
			if ((stk[i]|can[1])==can[1])
				dp[1][i][1]=num[i];

		for (i=2;i<=n;i++)
		{
			int a=i&1;
			memset(dp[a],0,sizeof(dp[a]));
			for (j=1;j<=top;j++)
				if ((stk[j]|can[i])==can[i])
					for (k=1;k<=top;k++)
					{
						if ((stk[j]&(stk[k]<<1)) || ((stk[j]<<1)&stk[k]))
							continue;
						if ((stk[k]|can[i-1])!=can[i-1])
							continue;
						for (t=1;t<=top;t++)
						{
							if (stk[j]&stk[t])
								continue;
							if (i>=3 && (stk[t]|can[i-2])!=can[i-2])
								continue;
							dp[a][j][k]=max(dp[a][j][k],dp[a^1][k][t]+num[j]);
						}
					}
		}
		for (i=1;i<=top;i++)
			for (j=1;j<=top;j++)
				ans = max(ans,dp[n&1][i][j]);
		printf("%d\n",ans);
	}
	return 0;
}

Hdu 1429 胜利大逃亡(续)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1429

思路:这个题不是dp,是状态压缩+Bfs,算是状态压缩的应用吧。

#include <cstdio>
#include <cstring>
#include <queue>
#include <cctype>
using namespace std;

const int limit=1<<10;
const int N=21;
int n,m,all;

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

struct Point
{
	int x,y;
	int step,key;

	void Get (int _x,int _y,int _step,int _key)
	{
		x=_x;
		y=_y;
		step=_step;
		key=_key;
	}
}now,temp;

char g[N][N];
bool visit[limit][N][N];

int Change (char c)
{
	return c-(isupper(c)?'A':'a');
}

int OK (int x,int y,int key)
{
	if (x<0 || x>=n || y<0 || y>=m)
		return false;
	if (g[x][y]=='*')
		return false;
	if (visit[key][x][y])
		return false;
	return true;
}

int Bfs (int sx,int sy)
{
	queue <Point> q;
	visit[0][sx][sy]=true;
	temp.Get (sx,sy,0,0);
	q.push(temp);
	while (!q.empty())
	{
		Point now=q.front();
		q.pop();
		if (now.step>=all)   //剪枝
			break;
		if (g[now.x][now.y]=='^')
			return now.step;
		for (int i=0;i<4;i++)
		{
			int x=now.x+dx[i];
			int y=now.y+dy[i];
			int t=now.step+1;
			int key=now.key;
			if (OK(x,y,key)==false)
				continue;
			if (isupper(g[x][y]) && (key & (1<<Change(g[x][y])))==0)   //没有钥匙
				continue;
			if (islower(g[x][y]))  //得到钥匙
				key|=(1<<Change(g[x][y]));
			visit[key][x][y]=true;
			temp.Get (x,y,t,key);
			q.push(temp);
		}
	}
	return -1;
}

int main ()
{
	while (~scanf("%d%d%d",&n,&m,&all))
	{
		memset(visit,false,sizeof(visit));
		int sx,sy;
		for (int i=0;i<n;i++)
		{
			scanf("%s",g[i]);
			for (int j=0;j<m;j++)
				if (g[i][j]=='@')
					sx=i,sy=j;
		}
		printf("%d\n",Bfs(sx,sy));
	}
	return 0;
}

Hdu 1074 Doing Homework

这个题没能独立完成…………

题意和思路可以参考这里:http://www.cnblogs.com/AndreMouche/archive/2011/01/28/1946997.html

#include <cstdio>
#include <cstring>

const int N =1<<16;

struct Node
{
	int cost;//所需要的时间
	int pre;//前一状态
	int reduced;//最少损失的分数
}dp[N];

bool visit[N];//表示完成j的状态是否被访问
int n;

struct Data
{
	int deadtime;//截止日期
	int cost;//所需日期
	char name[105];

	void Get ()
	{
		scanf("%s%d%d",name,&deadtime,&cost);
	}
}data[16];

void Output (int status)//递归输出课程安排表
{
	int curjob= dp[status].pre^status;
	int id=0;
	curjob>>=1;
	while (curjob)
		id++,curjob>>=1;
	if (dp[status].pre!=0) //输出其前面的课程
		Output (dp[status].pre);
	printf("%s\n",data[id].name);
}

void Input ()
{
	scanf("%d",&n);
	for (int i=0;i<n;i++)
		data[i].Get ();
	memset(visit,false,sizeof(visit));
	memset(dp,0,sizeof(dp));
}

int main ()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		Input ();
		int limit=(1<<n)-1;

		for (int i=0;i<=limit;i++)
			for (int work=0;work<n;work++)
			{
				int cur=1<<work;
				if ((cur & i)==0)//该项作业尚未做过
				{
					int last=cur|i;  //做该项作业之后的状态
					int day=dp[i].cost+data[work].cost;
					dp[last].cost=day;
					int reduce = day-data[work].deadtime;
					if (reduce<0)
						reduce=0;
					reduce+=dp[i].reduced;  //做完后超过的时间
      
					if (visit[last])//该状态已有访问信息
					{
						if (reduce<dp[last].reduced)
						{
							dp[last].reduced=reduce;
							dp[last].pre=i;
						}
		/*				else //扣分相同,取字典序小的那一个,由于这里j是按从小到达搜索的,默认已是按字典序,不需再处理
							if (reduce==dp[last].reduced && dp[last].pre>i)
									dp[last].pre=i;
						*/
					}
					else
					{
						visit[last]=true;
						dp[last].reduced=reduce;
						dp[last].pre=i;
					}
				}
			}
		printf("%d\n",dp[limit].reduced);
		Output (limit);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值