1. 程式人生 > 實用技巧 >Educational Codeforces Round 93 (Rated for Div. 2)(A-D:三維DP)

Educational Codeforces Round 93 (Rated for Div. 2)(A-D:三維DP)

A:http://codeforces.com/contest/1398/problem/A

題意:

在非遞減序列中找非法三角形

解析:

剛開始搞了個結構體排序,然後才發現給出的就是非遞減。。。

看1,2,n,如果它三能組成三角形,一定不存在非法。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+20;
ll a[maxn];
struct node
{
    int x;
    int id;
}st[maxn];
bool cmp(node a,node b)
{
    
return a.x<b.x; } int main() { int t; cin>>t; while(t--) { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } int x1=a[1],x2=a[2],x3=a[n]; if((x1+x2)>x3&&(x3-x1)<x2&&(x3-x2)<x1&&(x2-x1)<x3) cout
<<"-1"<<endl; else cout<<"1 2 "<<n<<endl; } }

B:http://codeforces.com/contest/1398/problem/B

題意:

01字串。

操作是每次刪除一串連續1,得分為連續1的數目。兩人依次操作,求先手的得分。

解析:

把所有連續1收集起來,排序,從大到小拿即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+20
; int a[maxn]; int b[maxn]; struct node { int x; int id; }st[maxn]; bool cmp(node a,node b) { return a.x<b.x; } int main() { int t; cin>>t; while(t--) { string s; cin>>s; int le=s.length(); int cnt=0,ok=0,tot=0; for(int i=0;i<le;i++) { if(s[i]=='1') { int md=1,k; int ok2=0; if(i<le-1){ for(int j=i+1;j<le;j++) { if(s[j]=='1') md++; else { k=j; ok2=1; break; } } } if(!ok) { // cnt+=md; ok=1; } else ok=0; b[tot++]=md; if(!ok2) { break; } i=k-1; } } ok=0; cnt=0; sort(b,b+tot); for(int i=tot-1;i>=0;i--) { if(!ok) { cnt+=b[i];ok=1; } else ok=0; } cout<<cnt<<endl; } }

C:http://codeforces.com/contest/1398/problem/C

題意:

求所有好序列的數目。

好序列:sum[j]-sum[i]==j-i+1

解析:

很明顯,先把字首和弄出來。

有:sumj-sum(i-1)==j-i+1

考慮把所有數字均-1,那麼變成:sumj-sum(i-1)-(j-i+1)=0

那麼就是求:所有區間和為0的區間數。

n*n很明顯行不通。

如果某個sum出現多次,那麼這一次與上一次的區間和就為0,與上上一次的區間和也為0,那麼總的可構造的數目就是:cnt+=sum之前出現的數目

map記錄次數,記得初始化mp[0]==0。

#include<bits/stdc++.h>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=1e5+20;
const ll inf=0x3f3f3f3f3f3f3f3f;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        map<ll,int>m;
        string s;
        cin>>s;
        m[0]++;
        ll sum=0;
        ll cnt=0;
        for(int i=0;i<s.length();i++)
        {
            sum+=s[i]-'1';  //-1
            cnt+=m[sum];
            m[sum]++;
        }
        cout<<cnt<<endl;
    }
}

D:http://codeforces.com/contest/1398/problem/D

題意:

給定若干個紅色,綠色,藍色的一對長度一樣的棍子.問用這些棍子組成的顏色不同的矩形的面積的最大總和是多少.注意不能把兩個相同顏色的一對棍子拆成兩個分別去用.其次顏色不同指的是在兩個集合裡選的兩對棍子.

解析:

考慮貪心,但是寫著很麻煩。。

就看了題解,原來是個三維DP

定義dp[i][j][k],表示紅色用了i個,綠色用了j個,藍色用了k個的前提下,所獲得的最大矩形面積和。

理解這個轉移方程,還是之前的dp思路:選的大還是不選的大?

#include<bits/stdc++.h>
#include<map>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=2e2+20;
//const ll inf=0x3f3f3f3f3f3f3f3f;
int r[maxn],g[maxn],b[maxn];
ll dp[maxn][maxn][maxn];
int main()
{
    int c1,c2,c3;
    cin>>c1>>c2>>c3;
    for(int i=1;i<=c1;i++)    cin>>r[i];sort(r+1,r+1+c1);
    for(int i=1;i<=c2;i++)    cin>>g[i];sort(g+1,g+1+c2);
    for(int i=1;i<=c3;i++)    cin>>b[i];sort(b+1,b+1+c3);
    ll maxx=0;
    for(int i=0;i<=c1;i++)
    {
        for(int j=0;j<=c2;j++)
        {
            for(int k=0;k<=c3;k++)
            {
                if(i&&j)    dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k]+r[i]*g[j]);
                if(i&&k)    dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]+r[i]*b[k]);
                if(j&&k)    dp[i][j][k]=max(dp[i][j][k],dp[i][j-1][k-1]+g[j]*b[k]);
                maxx=max(maxx,dp[i][j][k]);
            }
        }
    }
    cout<<maxx<<endl;
}