1. 程式人生 > 實用技巧 >7.30 2020 Multi-University Training Contest 4題解及補題

7.30 2020 Multi-University Training Contest 4題解及補題

目錄

7.30 2020 Multi-University Training Contest 4題解及補題

比賽過程

開局1011找到規律還是比較順利的,1002殺掉100血,情況欠考慮,1005注意分好情況

題解

1002 Blow up the Enemy

題意

有對父子在玩槍戰遊戲,有 n 把槍,,每把槍都有兩個屬性 A :攻擊力 ,D:冷卻時間。

初始都有 100 滴血,父親會隨機從 n 把槍裡選擇一把槍。

關於比賽有如下規定:

  1. 他倆第一槍一定是同時開的
  2. 能開槍就開槍
  3. 如果兩人同時死亡,各有 50 % 的概率獲勝

現在要你為兒子選擇一把槍,使得其勝利的概率最大,輸出這個概率

解法

直接判斷殺掉 100 滴血最快的槍有幾把,輸出 \(1.0−x/2n\)

程式碼

#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
using namespace std;
#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int inf = 0x3f3f3f3f;
typedef long long ll;
const int maxn = 100500;
const ll mod = 1e9 + 7;
typedef pair<int,int> pii;
const double pi = acos(-1);
struct node
{
    int x,y;
    // double c;
    int c;
}a[2005];
bool cmp(node a,node b) {
    return a.c<b.c;
}
int main() {
    IO;
    int T,n;
    cin>>T;
    while(T--) {
        cin>>n;
        bool flag=false;
        int cnt=0;
        for(int i=0;i<n;i++) {
            cin>>a[i].x>>a[i].y;
            int cn=100-a[i].x;
            if(a[i].x>=100) {flag=true; cnt++;}
            // a[i].c=double(a[i].x*1.0/a[i].y);
            if(cn>0) {
                if(cn%a[i].x!=0) {
                    a[i].c=a[i].y*(cn/a[i].x+1);
                }
                else {
                    a[i].c=a[i].y*(cn/a[i].x);
                }
            }
        }
        double ans=0.0;
        if(flag==false) {
            int tem=1;
            sort(a,a+n,cmp);
            int te=a[0].c;
            for(int i=1;i<n;i++) {
                if(a[i].c==te) {
                    // cout<<a[i].c<<" "<<te<<endl;
                    tem++;
                }
                else break;
            }
            ans=1.0-(0.5*tem*1.0/(n*1.0));
        }
        else {
            ans=1.0-(0.5*cnt*1.0/(n*1.0));
        }
        cout<<fixed<<setprecision(6)<<ans<<endl;
    }
    return 0;
}

1003 Contest of Rope Pulling

題意

T 組資料,有n,m代表每個班級的人數,每個人有兩種屬性wi(力量值),vi(魅力值),
問你從兩班中選擇兩個子集(可為空),使得兩個子集的力量值和相等,求選出來的所有人的魅力值和的最大值

解法

可以將兩班人和為一個集合,需要將第二班的力量改為負數。之後進行dp,那麼dp[0]就是答案,由於下標可能是負數所以需要加一個數表示0
需要注意的是初始化為-INF,其次就是注意一下不要超過陣列的範圍。

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 3e3 + 7;
const ll M = 1e5 + 7;
const ll INF = 1e18;
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
int t;
int n, m;
struct node{
    ll w, v;
};
node a[N];
ll dp[M];

ll getdp(int n)
{
    fill(dp, dp + M, -INF);
    dp[50000] = 0;

    for (int i = 1; i <= n;i++)
    {
        if(a[i].w >= 0)
        {
            for (int j = 1e5; j >= a[i].w;j--)
            {
                if(dp[j - a[i].w] != -INF) dp[j] = max(dp[j], dp[j - a[i].w] + a[i].v);
            }
        }
        else
        {
            int lim = 1e5 + a[i].w;
            for (int j = 0; j <= lim; j++)
            {
                if (dp[j - a[i].w] != -INF) dp[j] = max(dp[j], dp[j - a[i].w] + a[i].v);
            }
        }
        
    }
    return dp[50000];

}

int main()
{
    srand(time(0));
    t = read();
    while(t--)
    {
        n = read(), m = read();
        for (int i = 1; i <= n + m;i++) 
        {
            a[i].w = read(), a[i].v = read();
            if(i > n) a[i].w = -a[i].w;
        }
        random_shuffle(a + 1, a + n + m + 1);
        ll ans = 0;
        ans = getdp(n + m);
        printf("%lld\n", ans);
    }
    return 0;
}

1004 Deliver the Cake

題意

給定一張無向圖,求從起點到終點的最短路徑。其中,每個點有一定限制,規定在該點時必須是以下三種狀態之一:L,R,M
經過L點時必須左手拿蛋糕,經過R點時必須右手拿蛋糕,經過M點時左手右手都可以。
在路徑中可以進行任意次換手,每次換手花費 x 。

解法

拆點+dij。這個題還是比較有意思的,我們也學習了拆點的技巧,將必須左手的村莊拆成兩個左手,右手拆成兩個右手,M的村莊拆成一左一右,然後跑dijsktra,然後因為是雙向邊,拆點後點數量變為原來2倍,連邊情況變為原來的4倍,所以存邊的陣列要開8倍;

程式碼

#include<bits/stdc++.h>
using namespace std;
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repp(i,a,b) for(int i=a;i<b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define perr(i,a,b) for(int i=a;i>b;i--)
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const ll mod=998244353;
const int maxn=200010;
char ss[maxn],sss[maxn];
int n,m,s,t,x;
ll dis[maxn];
int head[maxn],tot;
struct node
{
    int u,v,nxt;
    ll w;
}e[8*maxn];
struct qnode
{
    int v;
    ll dis;
    bool operator < (const qnode &r)const
    {
        return dis>r.dis;
    }
};
void add(int u,int v,ll w)
{
    e[tot].u=u;
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nxt=head[u];
    head[u]=tot++;

    e[tot].u=v;
    e[tot].v=u;
    e[tot].w=w;
    e[tot].nxt=head[v];
    head[v]=tot++;

}
void init()
{
    rep(i,1,2*n)head[i]=-1;
    tot=0;
}
ll dij(int s)
{
    rep(i,1,2*n)dis[i]=LINF;
    priority_queue<qnode>q;
    dis[s]=0;
    q.push({s,0});
    qnode tmp;
    while(!q.empty())
    {
        tmp=q.top();
        q.pop();
        int u=tmp.v;
        if(tmp.dis>dis[u])continue;
        for(int i=head[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].v;
            ll w=e[i].w;
            ll z=(sss[u]==sss[v]?0:x);
            if(dis[u]+w+z<dis[v])
            {
                dis[v]=dis[u]+w+z;
                q.push({v,dis[v]});
            }
        }
    }
    return min(dis[t],dis[t+n]);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d%d",&n,&m,&s,&t,&x);
        init();
        scanf("%s",ss);
        rep(i,1,n)
        {
            if(ss[i-1]=='M')sss[i]='L',sss[i+n]='R';
            else if(ss[i-1]=='L')sss[i]=sss[i+n]='L';
            else sss[i]=sss[i+n]='R';
        }
        while(m--)
        {
            int u,v;
            ll w;
            scanf("%d%d%lld",&u,&v,&w);
            add(u,v,w);add(u+n,v,w);
            add(u+n,v+n,w);add(u,v+n,w);
        }

        printf("%lld\n",min(dij(s),dij(s+n)));
    }
    return 0;

}

1005 Equal Sentences

題意

對句子S中的單詞向前移一位或向後移一位或不移得到的兩個句子幾乎相等,求多少不同的句子,幾乎等於S,包括S本身

解法

程式碼

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
int main(){
    ll t;
    scanf("%lld",&t);
    while(t--){
        ll n;
        scanf("%lld",&n);
        string s[n];
        ll a[n],c[n];
        for(ll i=0;i<n;i++){
            cin>>s[i];
            a[i]=0;
            c[i]=0;
        }
        if(n==1){
            cout<<1<<endl;
            continue;
        }
        ll ans=1;
        if(s[n-1]!=s[n-2]){
            c[n-2]=2;
        }
        else{
            c[n-2]=1;
        }
        if(n-2==0){
            cout<<c[0]<<endl;
            continue;
        }
        if(s[n-3]!=s[n-2]){
            c[n-3]=c[n-2]+1;
        }
        else{
            c[n-3]=c[n-2];
        }
        if(n-3==0){
            cout<<c[0]<<endl;
            continue;
        }
        for(ll i=n-4;i>=0;i--){
            if(s[i]!=s[i+1]){
                c[i]=(c[i+2]+c[i+1])%mod;
            }
            else{
                c[i]=c[i+1];
            }
        }
        cout<<c[0]<<endl;
    }
    return 0;
}

1007 Go Running

題意

有一條路可以看成是無盡頭的數軸,學生可以選擇一個點開始跑步,可以選擇從任意時間t1開始跑,從任意時間t2結束跑步,也可以選擇跑步方向,但跑步速度恆定為1 m/s
跑步開始前不會出現在數軸上,跑步結束後也不會出現在數軸上
這條路上有一些監控,你收到了n份報告,每份報告有兩個資料xi和ti,表示時間為ti秒時在數軸的xi位置有至少一個學生在跑步,問最少有多少個學生在跑步

解法

二分圖最大匹配問題
假設報告時刻為t,位置為x,那麼相同的t+x或者t−x能夠合併成一個人。可以用map記錄序號,將t+x和t−x相連,再用HK演算法求一下最大匹配就好了。

程式碼

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 5e5 + 10;
const int INF = 0x3f3f3f3f;
vector<int> G[maxn];
int uN;
int Mx[maxn], My[maxn];
int dx[maxn], dy[maxn];
int dis;
bool used[maxn];
bool SearchP() {
	queue<int>Q;
	dis = INF;
	memset(dx, -1, sizeof(dx));
	memset(dy, -1, sizeof(dy));
	for (int i = 1; i <= uN; i++)
		if (Mx[i] == -1) {
			Q.push(i);
			dx[i] = 0;
		}
	while (!Q.empty()) {
		int u = Q.front();
		Q.pop();
		if (dx[u] > dis)break;
		int sz = G[u].size();
		for (int i = 0; i < sz; i++) {
			int v = G[u][i];
			if (dy[v] == -1) {
				dy[v] = dx[u] + 1;
				if (My[v] == -1)dis = dy[v];
				else {
					dx[My[v]] = dy[v] + 1;
					Q.push(My[v]);
				}
			}
		}
	}
	return dis != INF;
}
bool DFS(int u) {
	int sz = G[u].size();
	for (int i = 0; i < sz; i++) {
		int v = G[u][i];
		if (!used[v] && dy[v] == dx[u] + 1) {
			used[v] = true;
			if (My[v] != -1 && dy[v] == dis)continue;
			if (My[v] == -1 || DFS(My[v])) {
				My[v] = u;
				Mx[u] = v;
				return true;
			}
		}
	}
	return false;
}
int MaxMatch() {
	int res = 0;
	memset(Mx, -1, sizeof(Mx));
	memset(My, -1, sizeof(My));
	while (SearchP()) {
		memset(used, false, sizeof(used));
		for (int i = 1; i <= uN; i++)
			if (Mx[i] == -1 && DFS(i))
				res++;
	}
	return res;
}
map<int, int> m1, m2;
int t, n, tt, xx;
int main() {
	scanf("%d", &t);
	while (t--) {
		memset(G, 0, sizeof(G));
		m1.clear();
		m2.clear();
		scanf("%d", &n);
		int u = 0, v = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%d%d", &tt, &xx);
			int x = tt + xx;
			int y = tt - xx;
			if (!m1[x]) m1[x] = ++u;
			if (!m2[y]) m2[y] = ++v;
			G[m1[x]].push_back(m2[y]);
		}
		uN = u;
		printf("%d\n", MaxMatch());
	}
	return 0;
}

1011 Kindergarten Physics

題意

質量a,b的物體,初始距離d米,c秒後的距離

解法

水題,直接輸出d或者,可以魔鬼-0.0000000001,小於精確度的

程式碼

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        double a,b,c,d;
        scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
        c-=0.0000000001;
        printf("%.10lf\n",c);
    }
}

1012 Last Problem

題意

給出一個無限大的二維平面,需要在平面內進行染色,每次可以選擇一個點 ( x , y ) 將其染色為 n 的前提是,相鄰四個格子必須分別已經染了 n - 1 , n - 2 , n - 3 , n - 4 四種顏色,染色可以覆蓋,構造出一種合理的染色方案,使得可以在平面上出現顏色 n

解法

構造題,對於某一時刻

​ n-1

n-2 n n-3

​ n-4

程式碼

#include <algorithm>
#include <bitset>
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <limits>
#include <list>
#include <map>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <utility>
#include <vector>
#include <cwchar>
#include <cwctype>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e3+100;
const int b[5][2]={0,0,0,-1,-1,0,1,0,0,1};
int maze[N][N];
int cnt=0;
void dfs(int x,int y,int val)
{
	if(val<=0)
		return;
	for(int i=1;i<=4;i++)
	{
		int xx=x+b[i][0],yy=y+b[i][1];
		if(maze[xx][yy]!=val-i)
			dfs(xx,yy,val-i);
	}
	maze[x][y]=val;
	printf("%d %d %d\n",x,y,val);
}
 
int main()
{
	int n;
	scanf("%d",&n);
	dfs(1000,1000,n);
    return 0;
}