1. 程式人生 > >Gym101482 NWERC 2014(隊內訓練第4場)

Gym101482 NWERC 2014(隊內訓練第4場)

amp type 比賽 lse 要求 scan rand() double sqrt

-----------------------前面的兩場感覺質量不高,就沒寫題解-----------------------------

C .Cent Savings

pro:按順序給定N個商品,相鄰的可以分到同一組,你可以最多分成D+1組,每一組的價格四舍五入,求最小價格。N<2e3,D<20;

sol:由於的相鄰的分組,就DP即可,O(N^2*D)。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int
maxn=2010; int dp[maxn][30];//dp[i][j] int a[maxn], sum[maxn]; inline f(int x){return (x / 10 * 10 + (x % 10 <= 4 ? 0 : 10));} int main() { int n, d; cin >> n >> d; for(int i = 1; i <= n; i++)cin >> a[i], sum[i] = a[i] + sum[i - 1]; for(int i = 1; i <= n; i++)dp[i][1
] = f(sum[i]); for(int j = 2; j <= d + 1; j++) { for(int i = j; i <= n; i++) { int tmp = 1e9 + 7; for(int k = j - 1; k <= i - 1; k++) { tmp = min(tmp, dp[k][j - 1] + f(sum[i] - sum[k])); } dp[i][j]
= tmp; } } int ans = 1e9 + 7; for(int i = 1; i <= d + 1 && i <= n; i++)ans = min(ans, dp[n][i]); cout<<ans<<endl; return 0; }

D .Digi Comp II

pro:給定M個開關,每個開關有初始狀態(L或者R),每個開關有兩個走向,分別指向左邊對應的開關和右邊對應的開關。 一個球走到當前開關,會走向當前狀態指向的方向,並且使當前的狀態改變。 問N個球從1號出發,最終每個開關的狀態。 給定的關系是個DAG,除了0號都有兩個出度,可以看成左右兒子。

sol:模擬一下,不難發現,如果X個球經過i節點,那麽將會有X/2+X&1次走向初始方向,X/2次走向另外一個方向。即是topo排序一下可以搞。

這題WA的蠻多的,有兩個註意;

1:不一定只要1號點入度為0,題目只保證了除0之外,出度為2;沒保證入度(這一點感覺題目的確誤導了很多讀題人)。 所以必須開始把入度為0的都加進queue去,不然有的點跑不到。

2:左右兒子一樣的情況下,如果先減左右兒子的入度,再入隊,會加兩次,導致錯誤。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=1000010;
int ind[maxn],ans[maxn];
int a[maxn],b[maxn],q[maxn],head,tail;
ll Now[maxn],N; char c[maxn][3];
int main()
{
    int M,u,v;
    scanf("%lld%d",&N,&M);
    rep(i,1,M){
        scanf("%s%d%d",c[i],&a[i],&b[i]);
        ind[a[i]]++; ind[b[i]]++;
    }
    Now[1]=N; rep(i,1,M) if(!ind[i]) q[++head]=i;
    while(tail<head){
        u=q[++tail];
        if(u==0) break;
        if(Now[u]%2==1) ans[u]=1;
        if(c[u][0]==L){
            Now[a[u]]+=(Now[u]-Now[u]/2);
            Now[b[u]]+=Now[u]/2;
        }
        else {
            Now[b[u]]+=(Now[u]-Now[u]/2);
            Now[a[u]]+=Now[u]/2;
        }
        ind[a[u]]--;
        if(ind[a[u]]==0) q[++head]=a[u];
        ind[b[u]]--;
        if(ind[b[u]]==0) q[++head]=b[u];
    }
    rep(i,1,M) {
        if(ans[i]) {
            if(c[i][0]==L) putchar(R);
            else putchar(L);
        }
        else {
            if(c[i][0]==R) putchar(R);
            else putchar(L);
        }
    }
    return 0;
}

E :Euclidean TSP

pro:雖然題目是TSP,但是和TSP沒什麽大關系。 就是給定飛機上升的時間和直線飛行的時間,二者和C的關系,求最小時間。

sol:飛機飛高需要時間,飛高之後飛得快,不難想到總時間隨高度先增後減,三分即可。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
double n, p, s, v;
const double eps = 1e9;
const double t = sqrt(2.0);
double f(double x)
{
    double tmp = n * pow(log(n) / log(2.0), t * x);
    tmp = tmp /(eps * p);
    tmp = tmp + s / v * (x + 1.0) / x;
    return tmp;
}
int main(){
    cin >> n >> p >> s >> v;
    double l =0, r = 100, ans=1e60, c;
    int T = 100;
    while(T--)
    {
        double lmid = l + (r - l) / 3.0;
        double rmid = r - (r - l) / 3.0;
        double Lans=f(lmid),Rans=f(rmid);
        if(Rans> Lans){
            r=rmid;
            if(ans>Lans) ans =Lans, c = lmid;
        }
        else{
            l = lmid;
            if(ans>Rans) ans =Rans, c = rmid;
        }
    }
    printf("%.9f %.9f\n", ans, c);
    return 0;
}

F .Finding Lines

pro:給定N個點,P; 問是否有超過P%的點在一條直線上。

sol:隨機兩個點,看多少點在直線上。(上海大都會原題)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
struct node
{
    ll x, y;
}c[maxn];
node a, b;
bool judge(node c)
{
    return (b.y - a.y) * (c.x - b.x) == (b.x - a.x) * (c.y - b.y);
}
int main()
{
    srand(time(0));
    int n, p;
    cin >> n >> p;
    for(int i = 1; i <= n; i++)cin >> c[i].x >> c[i].y;
    if(n <= 2)
    {
        cout<<"possible\n";
        return 0;
    }
    int T = 1000;
    bool flag = 0;
    while(T--)
    {
        int x = (ll)rand() * rand() % n + 1;
        int y = (ll)rand() * rand() % n + 1;
        if(x == y)continue;
        a = c[x], b= c[y];
        int tot = 0;
        for(int i = 1; i <= n; i++)if(judge(c[i]))tot++;
        if(1.0 * tot / n + 1e-9 >= 0.01 * p){flag = 1;break;}
    }
    if(flag)cout<<"possible"<<endl;
    else cout<<"impossible"<<endl;
    return 0;
}

G .Gathering

pro:給定二維平面上N個點,以及D。要求找一個點X,滿足X到N個點的曼哈頓距離都不超過D,而總距離最小。 不存在合法的輸出"impossible"

sol:因為我們知道中位數這個位置比較關鍵,X和Y可以分開考慮,分別是取中位數時最小,對X和Y都是凹函數。

比賽的時候大方向想對了。大概是曼哈頓距離轉化切比雪夫,然後套二分或者三分。 (但是居然求公共矩形不會了。。。賽後想不就是對R和U取min,對取L和U取max嗎。。。 然後我們在合法的多邊形裏三分套三分即可。 也可以三分之後直接找最接近中位數的Y即可。

曼哈頓轉切比雪夫: 原點A(X,Y)---->逆時針旋轉45度後,長度*sqrt2的點A‘(X+Y,X-Y);

切比雪夫轉曼哈頓: L<=x<=R ; D<=y<=U ; 現在:(L+D)/2<=x<=(R+U)/2,且x一定時滿足,L<=x+y<=R,D<=y<=U;

三分套三分:O(N(log1e9)^2~=9e7),會超時T37.

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=100010;
const ll inf=1e30;
ll x[maxn],y[maxn],D,ans=1e30,midy;
ll up=inf,dn=-inf,le=-inf,ri=inf; int N;
ll solvey(ll midx,ll Y)
{
    ll res=0;
    rep(i,1,N) res+=abs(midx-x[i])+abs(Y-y[i]);
    return res;
}
ll solvex(ll midx)
{
    ll yu=min(midx-dn,ri-midx);
    ll yd=max(midx-up,le-midx);
    if(yu<yd) return inf;
    ll res=inf,mid;
    while(yd<yu-2){
        ll l=yd+(yu-yd)/3,ans1=solvey(midx,l);
        ll r=yu-(yu-yd)/3,ans2=solvey(midx,r);
        if(ans1>ans2) yd=l,res=min(res,ans2);
        else yu=r,res=min(res,ans1);
    }
    rep(i,yd,yu) res=min(res,solvey(midx,i));
    return res;
}
int main()
{
    scanf("%d",&N);
    rep(i,1,N) scanf("%lld%lld",&x[i],&y[i]);
    scanf("%lld",&D);
    rep(i,1,N){
        dn=max(dn,x[i]-y[i]-D);
        up=min(up,x[i]-y[i]+D);
        le=max(le,x[i]+y[i]-D);
        ri=min(ri,x[i]+y[i]+D);
    }
    if(dn>up||le>ri) return puts("impossible"),0;
    sort(y+1,y+N+1); midy=(y[(N+1)/2]);
    ll L=(le+dn)/2,R=(up+ri)/2;
    while(L<R-2){
        ll l=L+(R-L)/3,ans1=solvex(l);
        ll r=R-(R-L)/3,ans2=solvex(r);
        if(ans1>ans2) L=l,ans=min(ans,ans2);
        else R=r,ans=min(ans,ans1);
    }
    rep(i,L,R) ans=min(ans,solvex(i));
    printf("%lld\n",ans);
    return 0;
}

三分+中位數逼近:沒必要取三分y,直接用結論即可,取靠近y[]的中位數。

技術分享圖片
#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=100010;
const ll inf=1e30;
ll x[maxn],y[maxn],D,ans=1e30,midy;
ll up=inf,dn=-inf,le=-inf,ri=inf; int N;
ll solve(ll midx)
{
    ll yu=min(midx-dn,ri-midx);
    ll yd=max(midx-up,le-midx);
    if(yu<yd) return inf;
    ll Y,res=0;
    rep(i,1,N) res+=abs(midx-x[i])+abs(Y-y[i]);
    return res;
}
int main()
{
    scanf("%d",&N);
    rep(i,1,N) scanf("%lld%lld",&x[i],&y[i]);
    scanf("%lld",&D);
    rep(i,1,N){
        dn=max(dn,x[i]-y[i]-D);
        up=min(up,x[i]-y[i]+D);
        le=max(le,x[i]+y[i]-D);
        ri=min(ri,x[i]+y[i]+D);
    }
    if(dn>up||le>ri) return puts("impossible"),0;
    sort(y+1,y+N+1); midy=(y[(N+1)/2]);
    ll L=(le+dn)/2,R=(up+ri)/2;
    while(L<R-2){
        ll l=L+(R-L)/3,ans1=solve(l);
        ll r=R-(R-L)/3,ans2=solve(r);
        if(ans1>ans2) L=l,ans=min(ans,ans2);
        else R=r,ans=min(ans,ans1);
    }
    rep(i,L,R) ans=min(ans,solve(i));
    printf("%lld\n",ans);
    return 0;
}
View Code

H .Hyacinth

pro:看不懂。

sol:by隊友

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
struct edge{
    int d,nex;
}e[20010];
struct node{
    int x,y;
    bool a,b;
}a[10010];
int q[10010],efree,n,cnt;
inline void add(int x,int y){e[++efree]=(edge){y,q[x]},q[x]=efree;}
void dfs(int x,int y){
    if(a[x].a==0)a[x].x=++cnt;
    if(a[x].b==0)a[x].y=++cnt;
    int v=q[x];while(e[v].d==y)v=e[v].nex;
    if(!v)return;
    if(a[x].a)a[e[v].d].x=a[x].y,a[e[v].d].a=a[x].b=1;
    else a[e[v].d].x=a[x].x,a[e[v].d].a=a[x].a=1;
    dfs(e[v].d,x);
    for(int i=e[v].nex;i;i=e[i].nex)
        if(e[i].d!=y){
            if(a[x].a)a[e[i].d].x=a[x].y,a[e[i].d].a=a[x].b=1;
            else a[e[i].d].x=a[x].x,a[e[i].d].a=a[x].a=1;
            dfs(e[i].d,x);
        }
}
int main(){
    scanf("%d",&n);
    if(n==2){
        puts("1 2");
        puts("2 1");
        return 0;
    }
    for(int i=1;i<n;i++){
        int x,y;scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)printf("%d %d\n",a[i].x,a[i].y);
    return 0;
}

J .Judging Troubles

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
map<string, int>Map1, Map2;
int main()
{
    int n, ans = 0;
    string s;
    cin >> n;
    for(int i = 1; i <= n; i++)cin >> s, Map1[s]++;
    for(int i = 1; i <= n; i++)cin >> s, Map2[s]++;
    for(map<string, int>::iterator it = Map1.begin(); it != Map1.end(); it++)
        ans += min(Map1[it->first], Map2[it->first]);
    cout<<ans<<endl;
    return 0;
}

Gym101482 NWERC 2014(隊內訓練第4場)