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

這可能是我做div2div2以來做的最好的一次?它居然unratedunrated了!哼╭(╯^╰)╮不給力的服務器!關鍵時刻拓機!雖然這次的D比較水。但是是我一次過了四題QAQ…
比賽傳送門:https://codeforces.com/contest/1106

A. Lunar New Year and Cross Counting

題解:數交叉XX個數,O(N2)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

題解:按題意模擬,用一個優先隊列維護當前最便宜並且indexindex最小的食物,如果當前顧客所需要的食物數量不夠,那就取poppop出來最便宜的食物,直到滿足當前顧客,如果滿足不了那就輸出00;如果足夠當前顧客需要的,那就直接輸出數量乘以花費即可。(Ps: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=1msj2\sum_{j = 1}^{m}{s_j}^2sjs_j爲第jj組的數字的和,一共有mm組。既然要最小化和,那麼就要最小化sj2{s_j}^2,我當時就猜測,肯定每組兩個數,讓每組最小的話肯定是最小和最大配,第二小和第二大配。然後莽了一發就A了。

題解:我們可以考慮兩個數,a,ba,b,答案只能是(1)(a+b)2(a + b)^2\tag 1四個數,a,b,c,da,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)a2+b2+c2+d2+2(ab+cd)a^2+b^2+c^2+d^2+2(ab+cd)\tag 4
如果我們想要(4)(4)儘可能的小,那麼我們就需要最小化ababcdcd,由乘法的性質可得讓abs(ab)abs(a-b)abs(cd)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

題意BobBob在每個[si,ti][s_i,t_i]時間段內搶紅包,只要在當前時間xx可以搶紅包,那麼他就一定會去搶。然後沒搶完一個紅包,那麼他必須等到di+1d_i+1時間才能搶下一個紅包。期間AliceAlice最多可以mm次阻撓BobBob 在時間xx時搶紅包。問BobBob最少可以獲得多少紅包。

題解:我們可以將問題分爲兩個部分,沒有AliceAlice阻撓,BobBob所能獲得的最少收益。和有AliceAlice不超過mm次時間點阻撓最少所能獲得的收益。
那麼第一部分是可以通過優先隊列預處理出來的,同時我們記錄每個時間點的收益,和下一次搶紅包的時間點。然後對於不超過mm次的阻撓,我們可以用動態規劃來解決。dp[i][j]dp[i][j]表示在ii時間時jj次阻撓最少所能獲得的收益。我們發現狀態的轉移有i1i-1nxt[i]nxt[i]兩種,並且每次搶完紅包必須等到di+1d_i+1才能搶,也就意味着下一次搶紅包時間是大於你的決策時的時間的,因此我們不妨考慮倒過來轉移。(實際上正着來也是可以的,而且官方題解是用的滾動數組)就有{dp[i][j]=min(dp[i][j],dp[nxt[i]][j]+got[i])dp[i][[j]=min(dp[i][j],dp[i+1][j1])\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}
最後答案就是min{dp[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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章