2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) 部分题解

Problem A. Altruistic Amphibians

dp[i]表示当前至多放重量为i的青蛙的底座的最大高度。

按重量从大到小排序后dp即可。

#include
using namespace std ;
int dp[100000000 + 10] ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , d ;
    cin >> n >> d ;
    vector> v(n) ;
    for(int i = 0 ; i < n ; i ++)
    {
        //l w h
        for(int j = 0 ; j < 3 ; j ++)  cin >> v[i][j] ;
        //w l h
        swap(v[i][0] , v[i][1]) ;
    }
    sort(v.begin() , v.end()) ;
    reverse(v.begin() , v.end()) ;
    int ans = 0 ;
    for(int i = 0 ; i < n ; i ++)
    {
        int w = v[i][0] ;
        int l = v[i][1] ;
        int h = v[i][2] ;
        if(dp[w] + l > d)  ans ++ ;
        for(int j = 1 ; j <= w - 1 && j + w <= 100000000 ; j ++)
            dp[j] = max(dp[j] , dp[j + w] + h) , dp[j] = min(dp[j] , d + 1) ;
    }
    cout << ans << '\n' ;
    return 0 ;
}

 

Problem B. Baby Bites

温暖的签到题。

#include
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    cin >> n ;
    bool flag = true ;
    for(int i = 1 ; i <= n ; i ++)
    {
        string s ;
        cin >> s ;
        if(s[0] >= '1' && s[0] <= '9')
        {
            int now = 0 ;
            for(int i = 0 ; i < s.size() ; i ++)
                now = now * 10 + (s[i] - '0') ;
            if(now != i)  flag = false ;
        }
        else if(s[0] == '0')  flag = false ;
    }
    if(flag)  cout << "makes sense\n" ;
    else  cout << "something is fishy\n" ;
    return 0 ;
}

 

Problem C. Code Cleanups

温暖的签到题。

#include
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    cin >> n ;
    vector vis(366 , false)  ;
    for(int i = 0 ; i < n ; i ++)
    {
        int x ;
        cin >> x ;
        vis[x] = true ;
    }
    int ans = 0 ;
    vector res ;
    function ok = [&](int u)
    {
        if(res.size() == 0)  return false ;
        if(u == 365)  return true ;
        int sum = 0 ;
        for(auto x : res)  sum += u - x ;
        return sum + res.size() >= 20 ;
    } ;
    for(int i = 1 ; i <= 365 ; i ++)
    {
        if(vis[i])  res.push_back(i) ;
        if(ok(i))  res.clear() , ans ++ ;
    }
    cout << ans << '\n' ;
    return 0 ;
}

 

Problem D. Delivery Delays

二分答案。dp[i][j]表示取过前i个菜且送过前j个菜且当前位于u[j]的最小时间。通过二分答案的最大等待时间来保证合法性。

#include
using namespace std ;
const int maxn = 1000 + 10 ;
typedef long long ll ;
int n , m ;
vector> g[maxn] ;
long long dis[maxn][maxn] ;
long long dp[maxn][maxn] ;
typedef pair pli ; //±ßȨlong long
int k ;
long long s[maxn] , u[maxn] , t[maxn] ;
struct Dij
{
    long long dis[maxn] ;
    priority_queue , greater > q ;
    void init()
    {
        memset(dis , 0x3f , sizeof(dis)) ; 
    }
    void dijkstra(int s)
    {
        dis[s] = 0 ;
        q.push(make_pair(0 , s)) ;
        while(!q.empty())
        {
            pli p = q.top() ;
            q.pop() ;
            int u = p.second ;
            if(p.first != dis[u])  continue ; //ÓÅ»¯£¬²»ÓþÉÖµ¸üС£
            for(auto x : g[u])
            {
                int v = x.first ;
                long long w = x.second ;
                if(dis[v] > dis[u] + w)
                {
                    dis[v] = dis[u] + w ;
                    q.push(make_pair(dis[v] , v)) ;
                }
            }
        }
    }
} dij ;
bool ok(long long up)
{
    //dp[i][j]±íʾÄÃÁËÇ°i¸öÎïÆ·ÇÒËÍÁËÇ°j¸öÎïÆ·£¬ÇÒµ±Ê±ÔÚu[j]µÄ×îСʱ¼ä
    memset(dp , 0x3f , sizeof(dp)) ;
    for(int i = 1 ; i <= k ; i ++)  dp[i][0] = t[i] ;
    for(int i = 1 ; i <= k ; i ++)
        for(int j = 0 ; j <= i ; j ++)
        {
            if(dp[i][j] > 1e16)  continue ;
            //cout << i << ' ' << j << ' ' << dp[i][j] << '\n' ;
            if(i == j)
            {
                long long res = dp[i][j] + dis[u[j]][1] ;
                for(int p = i + 1 ; p <= k ; p ++)
                {
                    long long cost = max(res , t[p]) + dis[1][u[j + 1]] ;
                    if(cost <= s[i + 1] + up)
                        dp[p][i + 1] = min(dp[p][i + 1] , cost) ;
                }
            }
            else
            {
                long long cost = dp[i][j] + dis[u[j]][u[j + 1]] ;
                if(cost < dp[i][j + 1] && cost <= s[j + 1] + up)
                {
                    dp[i][j + 1] = cost ;
                }
            }
        }
    return dp[k][k] < 1e16 ;
}
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    cin >> n >> m ;
    for(int i = 1 ; i <= m ; i ++)
    {
        int u , v , w ;
        cin >> u >> v >> w ;
        g[u].push_back({v , w}) ;
        g[v].push_back({u , w}) ;
    }
    for(int i = 1 ; i <= n ; i ++)
    {
        dij.init() ;
        dij.dijkstra(i) ;
        for(int j = 1 ; j <= n ; j ++)  dis[i][j] = dij.dis[j] ;
    }
    u[0] = 1 ;
    cin >> k ;
    for(int i = 1 ; i <= k ; i ++)  cin >> s[i] >> u[i] >> t[i] ;
    long long ans = 1e16 ;
    long long l = 0 , r = 1e16 ;
    while(l <= r)
    {
        long long mid = (l + r) / 2 ;
        if(ok(mid))  ans = mid , r = mid - 1 ;
        else  l = mid + 1 ;
    }
    cout << ans << '\n' ;
    return 0 ;
}

 

Problem E. Explosion Exploit

你有n个小兵,对手有m个小兵,告诉你每个小兵当前的hp,每一轮会随机一个小兵使其hp-1,当对手的m个小兵的血量全为0时结束,问你d轮前结束的概率

,直接状压dp的话复杂度是6^{10}无法承受,容易发现顺序是无关的,所以按照hp升序后状压即可,本质不同的状态数只有300*300多

#include 
#define fi first
#define se second
using namespace std;
using pii = pair;
using db = long double;
int n, m, d, hp1[5], hp2[5], dp[6][7];
int encode(int *a, int n)
{
    static int b[5];
    memcpy(b, a, n<<2);
    sort(b, b+n);
    int x = 0;
    for(int i=0; i=0; i--) a[i] = x%10, x /= 10;
}
int main()
{
    scanf("%d%d%d", &n, &m, &d);
    for(int i=0; i cur, nxt;
    cur[{encode(hp1, n), encode(hp2, m)}] = 1.0;
    db ans = 0.0;
    while(d--)
    {
        //cout << d << ' ' << cur.size() << '\n';
        for(auto it : cur)
        {
            decode(hp1, n, it.fi.fi), decode(hp2, m, it.fi.se);
            int alive = 0, cnt = 0;
            for(int i=0; i0);
            for(int i=0; i0), cnt += hp2[i];
            if(cnt>d+1) continue;
            for(int i=n-1; i>=0; i--)
            {
                if(hp1[i])
                {
                    --hp1[i];
                    nxt[{encode(hp1, n), it.fi.se}] += it.se*(1.0/alive);
                    ++hp1[i];
                }
                else break;
            }
            for(int i=m-1; i>=0; i--)
            {
                if(hp2[i])
                {
                    --hp2[i];
                    int tmp = encode(hp2, m);
                    if(!tmp) ans += it.se*(1.0/alive);
                    else nxt[{it.fi.fi, tmp}] += it.se*(1.0/alive);
                    ++hp2[i];
                }
                else break;
            }
        }
        swap(nxt, cur);
        nxt.clear();
    }
    printf("%.8Lf\n", ans);
    return 0;
}

 

Problem H. House Lawn

满足\frac{t+r}{\frac{t}{\frac{l}{c}}} \leqslant 10080即可。

#include
using namespace std ;
int main()
{
    int l , m ;
    scanf("%d%d" , &l , &m) ;
    vector> ans ;
    int mn = 1e9 ;
    getchar() ;
    while(m --)
    {
        vector v ;
        string s = "" ;
        while(1)
        {
            char c = getchar() ;
            if(c == '\n')  break ;
            if(c == ',')  
            {
                v.push_back(s) ;
                s = "" ;
                continue ;
            }
            s += c ;
        }
        v.push_back(s) ;
        //cout << v.size() << '\n' ;
        assert(v.size() == 5) ;
        array a ;
        for(int i = 1 ; i < 5 ; i ++)
        {
            int now = 0 ;
            for(auto u : v[i])  now = now * 10 + (u - '0') ;
            a[i - 1] = now ;
        }
        function ok = [&]()
        {
            double c = a[1] ;
            double t = a[2] ;
            double r = a[3] ;
            double res = (t + r) / (t / (l / c)) ;
            return res < 10080 || fabs(res - 10080) < (1e-6) ;
        } ;
        if(ok())  mn = min(mn , a[0]) , ans.push_back({v[0] , a[0]}) ;
    }
    for(auto u : ans)  if(u.second == mn)  printf("%s\n" , u.first.c_str()) ;
    if(ans.size() == 0)  puts("no such mower") ;
    return 0 ;
}

 

Problem I. Intergalactic Bidding

有n个物品,问你能否选择一个集合使得价值恰好为x,一个特殊性质是大的物品的价值至少是小的物品的价值的两倍

从大到小考虑每个物品,如果当前物品的价值小于等于x,则必须选,否则剩下的物品全选也到不了x。价值很大,上个python。

n, s = map(int, input().split())
p = []
for _ in range(n):
    name, val = input().split()
    val = int(val)
    p.append((val, name))

p.sort(reverse=True)

choose = []
for item in p:
    if item[0] <= s:
        choose.append(item[1])
        s -= item[0]

if s==0:
    print(len(choose))
    for x in choose:
        print(x)
else:
    print(0)

 

Problem J. Jumbled String

通过a,d算出0,1的个数分别是num0,num1。容易发现b+c=num0*num1。我们把所有的0摆出来,然后把1插入空隙中即可。

注意特判a,b,c,d其中有0的情况。

#include
using namespace std ;
const int maxn = 1e5 + 10 ;
int cnt[maxn] ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int a , b , c , d ;
    cin >> a >> b >> c >> d ;
    function ok = [&](string s)
    {
        array f ;
        f[0] = f[1] = f[2] = f[3] = 0 ;
        for(int i = 0 ; i < s.size() ; i ++)
        {
            for(int j = i + 1 ; j < s.size() ; j ++)
                if(s[i] == '0' && s[j] == '0')  f[0] ++ ;
                else if(s[i] == '0' && s[j] == '1')  f[1] ++ ;
                else if(s[i] == '1' && s[j] == '0')  f[2] ++ ;
                else  f[3] ++ ;
        }
        //cout << f[0] << ' ' << f[1] << ' ' << f[2] << ' ' << f[3] << '\n' ;
        return f[0] == a && f[1] == b && f[2] == c && f[3] == d ;
    } ;
    if(a == 0 && d == 0)
    {
        if(ok("0"))  cout << "0\n" ;
        else if(ok("01"))  cout << "01\n" ;
        else if(ok("10"))  cout << "10\n" ;
        else if(ok("1"))  cout << "1\n" ;
        else  cout << "impossible\n" ;
        return 0 ;
    }
    else if(a == 0)
    {
        int num1 = -1 ;
        for(int i = 1 ; i * (i - 1) / 2 <= d ; i ++)
            if(i * (i - 1) / 2 == d)  num1 = i ;
        if(num1 == -1 || b + c > num1 || b + c > 0 && b + c < num1)
        {
            cout << "impossible\n" ;
            return 0 ;
        }
        if(b == 0 && c == 0)
        {
            for(int i = 1 ; i <= num1 ; i ++)  cout << "1" ;
            cout << '\n' ;
        }
        else 
        {
            for(int i = 1 ; i <= c ; i ++)  cout << "1" ;
            cout << "0" ;
            for(int i = 1 ; i <= b ; i ++)  cout << "1" ;
        }
        return 0 ;
    }
    else if(d == 0)
    {
        int num0 = -1 ;
        for(int i = 1 ; i * (i - 1) / 2 <= a ; i ++)
            if(i * (i - 1) / 2 == a)  num0 = i ;
        if(num0 == -1 || b + c > num0 || b + c > 0 && b + c < num0)
        {
            cout << "impossible\n" ;
            return 0 ;
        }
        if(b == 0 && c == 0)
        {
            for(int i = 1 ; i <= num0 ; i ++)  cout << "0" ;
            cout << '\n' ;
        }
        else
        {
            for(int i = 1 ; i <= b ; i ++)  cout << "0" ;
            cout << "1" ;
            for(int i = 1 ; i <= c ; i ++)  cout << "0" ;
        }
        return 0 ;
    }
    else
    {
        int num0 = -1 , num1 = -1 ;
        for(int i = 1 ; i * (i - 1) / 2 <= a ; i ++)
            if(i * (i - 1) / 2 == a)  num0 = i ;
        for(int i = 1 ; i * (i - 1) / 2 <= d ; i ++)
            if(i * (i - 1) / 2 == d)  num1 = i ;
        //cout << num0 << ' ' << num1 << '\n' ;
        if(num0 == -1 || num1 == -1 || num0 * num1 != b + c)
        {
            cout << "impossible\n" ;
            return 0 ;
        }
        for(int i = num0 ; i >= 1 ; i --)
        {
            while(b >= i)
            {
                cnt[i] ++ ;
                num1 -- ;
                b -= i ;
            }
        }
        cnt[0] = num1 ;
        for(int i = 0 ; i <= num0 ; i ++)
        {
            if(i > 0)  cout << "0" ;
            for(int j = 1 ; j <= cnt[i] ; j ++)  cout << "1" ;
        }
        cout << "\n" ;
    }
    return 0 ;
}

 

Problem K. King’s Colors

至多k的答案就是k*(k-1)^{n-1},求恰好那就套个二项式反演

#include 
using namespace std;
const int N = 2505, mod = 1e9 + 7;
int n, k, dp[N];
int Pow(int a, int b)
{
    int ans = 1;
    while(b)
    {
        if(b&1) ans = 1ll*ans*a%mod;
        a = 1ll*a*a%mod;
        b >>= 1;
    }
    return ans;
}
int fac[N], ifac[N];
void init(int n)
{
    fac[0] = 1;
    for(int i=1; i<=n; i++) fac[i] = 1ll*fac[i-1]*i%mod;
    ifac[n] = Pow(fac[n], mod-2);
    for(int i=n-1; i>=0; i--) ifac[i] = 1ll*ifac[i+1]*(i+1)%mod;
}
int C(int a, int b)
{
    if(a

 

你可能感兴趣的:(2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) 部分题解)