状压dp(不定期更新)

题目1:Hunter HDU - 4568

分析:TSP+最短路

这题算是很考验代码能力的题了(至少我写了一天多,tcl)
一共有k个点是宝藏,把外界看做一个点,则相当于k+1个点,最终我们就是求一个k+1个点的tsp(注意:入口和出口不一定一样)。因此需要先用dijkstra算法求出每个宝藏到其他k个点(包括外界)的最短距离——对每个点分别使用dijkstra算法。最短路求完之后我们保存的是不包含两端点权的最短距离,然后跑tsp,最后输出时记得加上所有宝藏的点权。

思考:

看了交大队长的博文,感觉这种题就是训练150行以内代码1Y的能力。如果写代码的时候手停3min,那么就说明思路在写之前不清晰,可以滚下机器去整思路了。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
const ll mod = 1e9 + 7;
const int maxn = 1e5;
const int inf = 0x3f3f3f3f;
struct node
{
	int x, y, v;
	node(int a = 0, int b = 0, int c = 0) : x(a), y(b), v(c){}
	bool operator < (const node& b) const
	{
		return b.v < v;
	}
}t[20];
int n, m, k;

int g[220][220], dis[220][220], G[20][20], flag[220][220], vis[220][220];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int out;

int dp[20000][20];

bool judge(int x, int y)
{
	return x >= 0 && x < n && y >= 0 && y < m;
}

void bfs(int num)
{
	mem(vis, 0);
	mem(dis, inf);
	dis[t[num].x][t[num].y] = g[t[num].x][t[num].y];
	priority_queue<node> q;
	q.push(t[num]);

	while(!q.empty())
	{
		node u = q.top(); q.pop();
		if(vis[u.x][u.y]) continue;
		vis[u.x][u.y] = 1;
		for(int i = 0; i < 4; i++)
		{
			int x = u.x + dir[i][0], y = u.y + dir[i][1];
			if(g[x][y] == -1) continue;
			if(!judge(x, y))
			{
				G[0][num] = G[num][0] = min(G[num][0], dis[u.x][u.y] - g[t[num].x][t[num].y]);
				continue;
			}
			if(dis[x][y] > dis[u.x][u.y] + g[x][y])
			{
				dis[x][y] = dis[u.x][u.y] + g[x][y];
				if(flag[x][y] > 0)
				{
					int id = flag[x][y];
					G[num][id] = min(G[num][id], dis[x][y] - g[t[num].x][t[num].y] - g[x][y]);
				}
				q.push(node(x, y, dis[x][y]));
			}
		}
	}
}

void tsp()
{
	mem(dp, inf);
	dp[1][0] = 0;
	for(int i = 1; i < (1 << k); i += 2)
		for(int j = 0; j < k; j++)
			if((i >> j) & 1)
			{
				for(int p = 0; p < k; p++)
					if(((i >> p) & 1) && p != j)
						dp[i][j] = min(dp[i][j], dp[i ^ (1 << j)][p] + G[j][p]);
			}
}

int main()
{
	int tt; cin >> tt;
	while(tt--)
	{
		cin >> n >> m;
		for(int i = 0; i < n; i++)
			for(int j = 0; j < m; j++)
				cin >> g[i][j];
		cin >> k; k++;
		mem(flag, 0);
		out = inf;
		for(int i = 0; i < k; i++)
			for(int j = 0; j < k; j++)
				G[i][j] = i == j ? 0 : inf;
		for(int i = 1; i < k; i++)
		{
			cin >> t[i].x >> t[i].y;
			t[i].v = g[t[i].x][t[i].y];
			flag[t[i].x][t[i].y] = i;
		}
		for(int i = 1; i < k; i++)
			bfs(i);
		tsp();
		int res = inf;
		int val = 0;
		for(int i = 1; i < k; i++)
		{
			res = min(res, dp[(1 << k) - 1][i] + G[i][0]);
			val += t[i].v;
		}
		if(res == inf) 
			cout << 0 << endl;
		else 
			cout << res + val << endl;
	}
}

题目2:郑厂长系列故事——N骑士问题 HDU - 4529

分析:

dp[i][j][k][p]表示考虑第i行时前i-1行已经用掉j个骑士且i-1行的二进制状态为k,i-2行的二进制状态为p时的方案数。对当前考虑的行,枚举所有状态,判断合法性,进行状态转移。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 lll;
#define print(i) cout << "debug: " << i << endl
#define close() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define mem(a, b) memset(a, b, sizeof(a))
const ll mod = 1e9 + 7;
const int maxn = 2e6;
const int inf = 0x3f3f3f3f;
int dp[2][11][300][300];
int num[300];
int g[20];
int flag1[300][300], flag2[300][300];
int n;

void init()
{
    for(int i = 0; i < (1 << 8); i++)
    {
        for(int j = 0; j <= 7; j++) num[i] += ((i >> j) & 1);
        for(int j = 0; j < (1 << 8); j++)
        {
            if(((i >> 2) & j) || ((i << 2) & j)) flag1[i][j] = 1;
            if(((i >> 1) & j) || ((i << 1) & j)) flag2[i][j] = 1;
        }
    }
}

ll solve()
{
    mem(dp, 0);
    dp[0][0][0][0] = 1;
    int now = 0;
    for(int i = 1; i <= 8; i++, now ^= 1)
    {
        for(int j = 0; j <= n; j++)
            for(int k = 0; k < (1 << 8); k++)
                for(int p = 0; p < (1 << 8); p++)
                {
                    if(dp[now][j][k][p] == 0) continue;
                    for(int q = 0; q < (1 << 8); q++)
                    {
                        if((q & g[i]) != q || (num[q] + j > n) || (i >= 2 && flag1[q][k]) || (i >= 3 && flag2[q][p])) 
                            continue;
                        else dp[now ^ 1][j + num[q]][q][k] += dp[now][j][k][p];
                    }
                }
        mem(dp[now], 0);
    }
    ll res = 0;
    for(int i = 0; i < (1 << 8); i++)
        for(int j = 0; j < (1 << 8); j++)   
            res += dp[now][n][i][j];
    return res;
}

int main()
{
    init();
    int t; cin >> t;
    while(t--)
    {
        cin >> n;
        mem(g, 0);
        for(int i = 1; i <= 8; i++)
        {
            char s[20]; cin >> s;
            for(int j = 0; j < 8; j++)
            {
                g[i] <<= 1;
                g[i] |= (s[j] == '.');
            }
        }
        cout << solve() << endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值