【01背包】Uvalive4015 Caves && Uvalive3637 The Bookcase

微笑我要相信LA已经挂掉了

微笑不管是我的代码还是网上的AC代码

微笑交上去全是WA WA WA!!!



so。。我也不知道我写的到底能不能过。。。以后找时间交吧。。

QAQ两道01背包。。当时学背包就没好好学T_T于是…………


Uvalive4015 Caves


题意:(不吐槽lrj那个语死早的翻译了)就是一个人要在树上走。。他可以在树上随意走动 然后有一些询问每次给出一个x 就是他每次在树上一共走x以内的距离(包含折返的)求最多能经过多少节点。。


由于这个x的范围真的是比较大。。。 = = 5 * 1e6...因为是树上所以一定会记录当前节点。。那么x放到状态里可能有点orz。。。。

然后窝就不会了T_T 然后。。发现可以把经过节点数放到状态里。。然后更新距离最后二分答案T_T wocccccc竟然又是被二分卡住了。。。


dp[u][i][j][k] 表示走到了u节点 已经考虑了i个儿子 还要走j个节点 是否不折返回u 的最小距离

转移:

dp[u][i][j][0] = min{ dp[u][i - 1][j - k][0] + dp[v][ch_sum][k][0] + 2 * e[i].w }

dp[u][i][j][1] = min{ dp[u][i - 1][j - k][0] + dp[v][ch_sum][k][1] + e[i].w, dp[u][i -1][j - k][1] + dp[v][ch_sum][k][0] + 2 * e[i].w }

(那个ch_sum就是表示儿子v的总儿子数。。)


这样的状态是开不下的。。。所以第二维拆掉滚动。。所以要倒序枚举j你造吧。。。

然后就这样吧。。昨天考试的时候顺带写的代码。。。读入输出优化占了大部分地方T_T。。。

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

int read()
{
	int n = 0, sign = 1; char c = getchar();
	while (c < '0' || c > '9') {if(c == '-') sign = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
	return n * sign;
}

void print(int x)
{
	if (x < 0) { putchar('-'); x *= -1; }
	if (!x) puts("0");
	else {
		int bit[15], len = 0;
		while (x) { bit[++len] = x % 10; x /= 10; }
		for (int i = len; i; --i) putchar(bit[i] + '0');
		puts("");
	}
}

const int Nmax = 505;

int N, root;
int dp[Nmax][Nmax][3];

struct ed{
	int v, w, next;
}e[Nmax];
int k = 1, head[Nmax];

inline void adde(int u, int v, int w)
{
	e[k] = (ed){ v, w, head[u] };
	head[u] = k++;
}

void dfs(int u)
{
	dp[u][1][0] = dp[u][1][1] = 0;
	for (int i = head[u]; i; i = e[i].next)
	{
		int v = e[i].v; dfs(v);
		for (int j = N; j > 1; j--)
		for (int k = 1; k <= j - 1; ++k)
		{
			dp[u][j][0] = min(dp[u][j][0], dp[u][j - k][0] + dp[v][k][0] + 2 * e[i].w);
			int temp = min(dp[u][j - k][0] + dp[v][k][1] + e[i].w, dp[u][j - k][1] + dp[v][k][0] + 2 * e[i].w);
			dp[u][j][1] = min(dp[u][j][1], temp);
		}
	}
}

int main()
{
	int cas = 0; while (scanf("%d", &N) && N)
	{
		memset(head, 0, sizeof(head)); k = 1; bool flag[Nmax];
		for (int i = 1; i < N; ++i)
		{
			int v = read(), u = read(), w = read();
			adde(u, v, w); flag[v] = 1;
		}
		for (int i = 0; i < N; ++i) if (!flag[i]) { root = i; break; }
		memset(dp, 0x3f, sizeof(dp)); dfs(root);
		
		printf("Case %d:\n", ++cas);
		for (int Q = read(); Q--; )
		{
			int x = read(), l = 1, r = N;
			while (r > l)
			{
				int mid = (l + r >> 1) + 1; 
				if (dp[root][mid][1] <= x || dp[root][mid][0] <= x) l = mid;
				else r = mid - 1;
			}
			print(l);
		}
	}
	
	return 0;
}


Uvalive3637 The Bookcase


题意:&……&*¥…@(*&¥*……%*&……#@#%%……不想写了……


最开始以为是斜率优化。。因为和之前那个打洞那个好像。。。后来发现不是。。。。

然后实在想不出去看了题解君。。。算了下复杂度把我吓到了。。。。

按照题解的做法。。。复杂度是70 * (70 * 30)^ 2的。。。这个复杂度。。。真的能跑起来。。。?

好吧我们就假设它可以吧。。。。。。


先把书从大到小排序 按顺序一本一本放 那么影响h的就只有第一本书了 放之后的书都只影响宽度了

然后就是暴力枚举了。。。 = =枚举三层的宽度。。不。。只用枚举两层。。第一层直接减一下就好了(其实感觉这样很多状态是没用的吧。。但是不知道怎么优化。。)

然后求出每种宽度的最小高度。。再求个最小值就行了。。。。。。

(这真的是dp????暴力真的不会更快????……)


首先假设第一本放在了第一层(其实具体放在哪一层不影响。。这个主要是为了保证每层都有书)

dp[i][j][k] 表示对于前i本书 第二层宽度为j 第三层宽度为k的 第二层和第三层的总高度的最小值

令 w = 第i本的宽度 h = 第i本的高度。那么转移:

放在第二层第一本: update( dp[i][w][k], dp[i -1][0][k] + h )

放在第三层第一本: update( dp[i][j][w], dp[i - 1][j][0] + h )

放在第二层后面: update( dp[i][j][k], dp[i - 1][j - w][k] )

放在第三层后面: update( dp[i][j][k], dp[i - 1][j][k - w] )

然后类似01背包。。。。把第一维拆掉倒序枚举j。。。。。


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

int read()
{
    int sign = 1, n = 0; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') sign = -1; c = getchar(); }
    while (c >= '0' && c <= '9') { n = n*10 + c-'0'; c = getchar(); }
    return n * sign;
}

typedef pair<int, int> book;
const int inf = 0x3f3f3f3f;

int N, sum, dp[2105][2105];
book b[75];

inline bool cmp(book a, book b) { return a.first > b.first; }
inline void update(int &a, int b, int c = inf) { a = min(a, min(b, c)); }

int main()
{
    for (int T = read(); T--; ) {
        N = read(); sum = 0;
        for (int i = 1; i <= N; ++i) {
            int hi = read(), wi = read();
            b[i] = book(hi, wi); sum += wi;
        }
        sort(b + 1, b + N + 1, cmp);
        
        memset(dp, 0x3f, sizeof(dp)); dp[0][0] = 0;
        for (int i = 2; i <= N; ++i) {
            for (int j = sum; ~j; --j) {
                for (int k = sum; ~k; --k) {
                    int temp1 = inf; int temp2 = inf;
                    if (j >= b[i].second) temp1 = (j == b[i].second) ? dp[0][k] + b[i].first : dp[j - b[i].second][k];
                    if (k >= b[i].second) temp2 = (k == b[i].second) ? dp[j][0] + b[i].first : dp[j][k - b[i].second];
                    update(dp[j][k], temp1, temp2);
                }
            }
        }
        
        int res = inf;
        for (int j = 1; j <= sum; ++j) {
            for (int k = 1; k <= sum; ++k) {
                if (dp[j][k] >= inf) continue;
                int w = max(max(j, k), sum - j - k);
                int h = dp[j][k] + b[1].first;
                update(res, w * h);
            }
        }
        printf("%d\n", res);
    }
    
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值