Codeforces Round #536 (Div. 2) A B C D E(dp)

这可能是我做 d i v 2 div2 以来做的最好的一次?它居然 u n r a t e d unrated 了!哼╭(╯^╰)╮不给力的服务器!关键时刻拓机!虽然这次的D比较水。但是是我一次过了四题QAQ…
比赛传送门:https://codeforces.com/contest/1106

A. Lunar New Year and Cross Counting

题解:数交叉 X X 个数, O ( N 2 ) O(N^2) 枚举中点即可。

代码

#include<bits/stdc++.h>

using namespace std;
string s[505];
int n;

int count1(int x,int y)
{
	if(s[x][y] != 'X') return 0;
	int dir[4][2] = {-1,-1,-1,1,1,-1,1,1};
	for(int i = 0; i < 4; ++i) {
		int tx = x + dir[i][0];
		int ty = y + dir[i][1];
		if(tx < 0 || tx >= n || ty < 0 || ty >= n)
			return 0;
		if(s[tx][ty] != 'X') return 0;
	}
	return 1;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
#endif
	cin >> n;
	for(int i = 0; i < n; ++i) {
		cin >> s[i];
	}
	int ans = 0;
	for(int i = 0; i < n; ++i) {
		for(int j = 0; j < n; ++j) {
			ans += count1(i,j);
		}
	}
	cout << ans << endl;
    return 0;
}

B. Lunar New Year and Food Ordering

题解:按题意模拟,用一个优先队列维护当前最便宜并且 i n d e x index 最小的食物,如果当前顾客所需要的食物数量不够,那就取 p o p pop 出来最便宜的食物,直到满足当前顾客,如果满足不了那就输出 0 0 ;如果足够当前顾客需要的,那就直接输出数量乘以花费即可。( P s : Ps: 千万要注意会爆long long!!)

代码


#include<bits/stdc++.h>

using namespace std;
const int N = 1E5+10;
struct node{
    int t,d;
}cust[N];

struct food{
    int c,d,id;
    bool operator < (const food & u) const {
        if(d == u.d) {
            return id > u.id;
        }
        return d > u.d;
    }
};
priority_queue<food> pq;
int c[N],d[N];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
#endif
    int n,m;
    cin >> n >> m;
    for(int i = 0; i < n; ++i) {
        scanf("%d",&c[i]);
    }
    for(int i = 0; i < n; ++i) {
        scanf("%d",&d[i]);
    }
    for(int i = 0; i < n; ++i) {
        pq.push(food{c[i],d[i],i});
    }
    for(int i = 0; i < m; ++i) {
        scanf("%d%d",&cust[i].t,&cust[i].d);
        cust[i].t--;
    }
    long long tmp = 0;
    for(int i = 0; i < m; ++i) {
        tmp = 0;
        int t = cust[i].t;
        int num = cust[i].d;
        if(num > c[t]) {
            tmp += 1LL * d[t] * c[t];
            num -= c[t];
            c[t] = 0;
            if(pq.empty()) tmp = -1;
            while(!pq.empty()) {
                if(tmp == -1) break;
                food cur = pq.top(); pq.pop();
                cur.c = min(cur.c,c[cur.id]);
                if(cur.c >= num) {
                    tmp += 1LL * cur.d * num;
                    cur.c -= num;
                    c[cur.id] -= num;
                    if(cur.c != 0)
                        pq.push(cur);
                    break;
                }else{
                    tmp += 1LL * cur.c * cur.d;
                    num -= cur.c;
                    c[cur.id] = 0;
                }
                if(pq.empty()) tmp = -1;
            }
        }else{
            c[t] -= num;
            tmp += 1LL * d[t] * num;
        }
        if(tmp == -1) tmp = 0;
        cout << tmp << endl;
    }
    return 0;
}

C. Lunar New Year and Number Division

说实话这题当时纯靠莽…,题意是给偶数个数,然后分组,每组至少两个,最后求最小化 j = 1 m s j 2 \sum_{j = 1}^{m}{s_j}^2 s j s_j 为第 j j 组的数字的和,一共有 m m 组。既然要最小化和,那么就要最小化 s j 2 {s_j}^2 ,我当时就猜测,肯定每组两个数,让每组最小的话肯定是最小和最大配,第二小和第二大配。然后莽了一发就A了。

题解:我们可以考虑两个数, a , b a,b ,答案只能是 (1) ( a + b ) 2 (a + b)^2\tag 1 四个数, a , b , c , d a,b,c,d 分一组 (2) ( a + b + c + d ) 2 = ( a + b ) 2 + ( c + d ) 2 + 2 ( a + b ) ( c + d ) (a + b + c + d)^2 = (a+b)^2+(c+d)^2+2(a+b)(c+d)\tag 2 分两组 (3) ( a + b ) 2 + ( c + d ) 2 (a + b)^2+(c+d)^2 \tag 3
对比 ( 2 ) ( 3 ) (2)(3) 显然每组的数量越少,总和越小。我们再观察 ( 3 ) (3) ,拆开:
(4) a 2 + b 2 + c 2 + d 2 + 2 ( a b + c d ) a^2+b^2+c^2+d^2+2(ab+cd)\tag 4
如果我们想要 ( 4 ) (4) 尽可能的小,那么我们就需要最小化 a b ab c d cd ,由乘法的性质可得让 a b s ( a b ) abs(a-b) a b s ( c d ) abs(c-d) 尽可能大即可。因此我们可以证明最小的和最大的配,第二小和第二大的配,以此类推的做法是正确的。

代码

#include<bits/stdc++.h>

using namespace std;
const int N = 3E5+10;
long long a[N];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
#endif
	int n;
	cin >> n;
	for(int i = 0; i < n; ++i) {
		scanf("%lld",&a[i]);
	}
	sort(a,a+n);
	long long sum = 0;
	for(int i = 0; i < n / 2; ++i) {
		sum += (a[i] + a[n - i - 1]) * (a[i] + a[n - i - 1]);
	}
	cout << sum << endl;
    return 0;
}

D. Lunar New Year and a Wander

题解:既然要访问的路径字典序最小,那么我们用优先队列+bfs就可以很好解决了。

代码

#include<bits/stdc++.h>
#define P pair<int,int>
using namespace std;
const int N = 1E5+10;
//vector<int> e[N];
priority_queue<int,vector<int>,greater<int> > e[N];
bool vis[N];
vector<int> ans;
priority_queue<int,vector<int>,greater<int> > pq;
void bfs()
{
    pq.push(1);
    vis[1] = 1;
    ans.push_back(1);
    while(!pq.empty()) {
        int cur = pq.top();
        if(vis[cur] == 0) {
            vis[cur] = 1;
            ans.push_back(cur);
        }
        pq.pop();
        for(; !e[cur].empty(); e[cur].pop()) {
            int v = e[cur].top();
            pq.push(v);
        }
    }
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
#endif
    int n,m,x,y;
    cin >> n >> m;
    for(int i = 0; i < m; ++i) {
        scanf("%d%d",&x,&y);
        e[x].push(y);
        e[y].push(x);
    }
    bfs();
    int len = ans.size();
    for(int i = 0; i < len; ++i) {
        printf("%d%c",ans[i],i==len-1?'\n':' ');
    }
    return 0;
}

E. Lunar New Year and Red Envelopes

题意 B o b Bob 在每个 [ s i , t i ] [s_i,t_i] 时间段内抢红包,只要在当前时间 x x 可以抢红包,那么他就一定会去抢。然后没抢完一个红包,那么他必须等到 d i + 1 d_i+1 时间才能抢下一个红包。期间 A l i c e Alice 最多可以 m m 次阻挠 B o b Bob 在时间 x x 时抢红包。问 B o b Bob 最少可以获得多少红包。

题解:我们可以将问题分为两个部分,没有 A l i c e Alice 阻挠, B o b Bob 所能获得的最少收益。和有 A l i c e Alice 不超过 m m 次时间点阻挠最少所能获得的收益。
那么第一部分是可以通过优先队列预处理出来的,同时我们记录每个时间点的收益,和下一次抢红包的时间点。然后对于不超过 m m 次的阻挠,我们可以用动态规划来解决。 d p [ i ] [ j ] dp[i][j] 表示在 i i 时间时 j j 次阻挠最少所能获得的收益。我们发现状态的转移有 i 1 i-1 n x t [ i ] nxt[i] 两种,并且每次抢完红包必须等到 d i + 1 d_i+1 才能抢,也就意味着下一次抢红包时间是大于你的决策时的时间的,因此我们不妨考虑倒过来转移。(实际上正着来也是可以的,而且官方题解是用的滚动数组)就有 { d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ n x t [ i ] ] [ j ] + g o t [ i ] ) d p [ i ] [ [ j ] = m i n ( d p [ i ] [ j ] , d p [ i + 1 ] [ j 1 ] ) \begin{cases}dp[i][j] =min(dp[i][j],dp[nxt[i]][j]+got[i])\\\\dp[i][[j] = min(dp[i][j],dp[i+1][j-1])\end{cases}
最后答案就是 m i n { d p [ 1 ] [ j ] } min\{dp[1][j]\}

代码

#include<bits/stdc++.h>
typedef long long LL;d
using namespace std;
const int N = 1E5+10;
struct node{
	int s,t,d,w;
	bool operator < (const node & u) const{
		if(w == u.w) return d < u.d;
		return w < u.w;
	}
};

bool cmp(const node & a, const node & b){ return a.s < b.s; }

priority_queue<node> pq;

vector<node> v;
LL dp[N][201], got[N], nxt[N];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
#endif
	int n,m,k,s,t,d,w;
	cin >> n >> m >> k;
	for(int i = 0; i < k; ++i) {
		cin >> s >> t >> d >> w;
		v.push_back(node{s,t,d,w});
	}
	sort(v.begin(),v.end(),cmp);
	int cur = 0;
	//1  4			
	//6  4 + 7      
	//11 4 + 7 + 9 <!> 11 <!>12 --> 4 + 7 = 11
	for(int i = 1; i <= n; ++i) {
		while(cur < k && v[cur].s <= i) 
			pq.push(v[cur++]);
		while(!pq.empty() && pq.top().t < i) pq.pop();
		if(!pq.empty()){
			node t = pq.top(); 
			got[i] = t.w;
			nxt[i] = t.d + 1;
		}else{
			got[i] = 0;
			nxt[i] = i + 1;
		}
	}
	memset(dp,0x3f,sizeof dp);
	dp[n + 1][0] = 0;
	for(int i = n; i > 0; --i) {
		dp[i][0] = dp[nxt[i]][0] + got[i];
	}
	for(int i = n; i >= 1; --i) {
		for(int j = 0; j <= m; ++j) {
			dp[i][j] = min(dp[i][j], dp[nxt[i]][j] + got[i]);
			dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
		}
	}
	LL ans = *min_element(dp[1],dp[2]);
	cout << ans << endl;
    return 0;
}

发布了219 篇原创文章 · 获赞 23 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Eternally831143/article/details/86741969
今日推荐