1. 程式人生 > 其它 >10.30多校聯訓

10.30多校聯訓

T1 刪數遊戲

Sol
直接全部數字加起來減一然後除以9就可以了,非常好證正確性。
Code
懶得粘。

T2 格點迷蹤

Sol
構造題。首先考慮直徑最小的構造方案:先看\(n,m\)中有至少一個為奇數:取出奇數那一維的中軸線,然後線上每個點向兩邊一直延伸即可。

每次延長直徑就把邊上的延伸線撇過來。

按照這個邏輯不斷撇就可以了。
如果\(n,m\)均為偶數,要注意無法實現直徑為對角線曼哈頓距離,要特判。
Code
\(Tell\ is\ easy,\ however\ I\ can't\ show\ you\ the\ code.\ Cause\ it's\ too\ hard\ for\ me\ to\ write\ that.\)

T3 沙漠綠洲

Sol
首先可以證明:每一條邊最多隻走一次。那麼其實就是花費\(dis(u,v)\)\(u,v\)聯通起來。
最開始我想的是直接列舉點集,然後跑\(kruskal\)計算答案取最值,然後發現\(n=2\)的小資料全過,大樣例死活過不了。
然後就發現可能選出來的點集並不只形成一個最小生成樹,而是一個最小生成森林。所以可以套上一個狀壓DP,把原來計算的答案改成只計算處在樹中的數的最小值。
這樣每種狀態表示對應二進位制位上是否被考慮。設\(g(now)\)表示當前狀態最優答案,\(f(now)\)表示當前狀態全部在同一樹上的答案。那麼答案有

\[g_{now}=\max_{i\&now=i} min(f_i,f_{now\ xor\ i}) \]

加一個記搜,時間複雜度\(O(2^{2n})\)

,這樣的話\(n=16\)會被卡,如何優化我也不知道。。。但是對於\(60\)分的優化是很好想的:因為所有點在同一數軸上,所以選擇的邊肯定是一段連續區間才最優,所以狀壓DP狀態列舉可以改成列舉區間起點終點,變成\(O(2^n*n^2)\)
Code(不帶優化)

#include<bits/stdc++.h>
using namespace std;
const int maxn=20;
int n;
double a[maxn],b[maxn],c[maxn];
double dis[maxn][maxn],rst;
bool con[maxn];
int fa[maxn],len,siz[maxn];
double cost[maxn],wat[maxn];
struct edge
{
    int from,to;
    double v;
    bool operator<(const edge &x)const
    {
        return v<x.v;
    }
}di[maxn*maxn];
inline int findf(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=findf(fa[x]);
}
double f[65546],g[65546];
inline void klske()
{
    for(int i=1;i<=n;i++)fa[i]=i,cost[i]=0,wat[i]=c[i],siz[i]=1;
    int now=1,al=0;
    for(int i=1;i<=n;i++)if(con[i])al++;
    if(al<2)
    {
        double an=1000000000.0;now=0;
        for(int i=1;i<=n;i++)
        {
            if(con[i])
            {
                now|=(1<<i-1);
                an=min(an,c[i]);
            }
        }
        f[now]=an;
        return;
    }
    for(int i=1;i<=len;i++)
    {
        if(con[di[i].from]==0||con[di[i].to]==0)continue;
        int x=di[i].from,y=di[i].to;
        double val=di[i].v;
        x=findf(x),y=findf(y);
        if(x==y)continue;now++;
        fa[x]=y;siz[y]+=siz[x];wat[y]+=wat[x];cost[y]+=cost[x]+val;
        if(now==al)break;
    }
    double an=1000000000.0,an1=1000000000.0;
    now=0;
    for(int i=1;i<=n;i++)
    {
        if(!con[i])continue;
        now|=(1<<i-1);
        if(fa[i]==i)an=min(an,(wat[i]-cost[i])/(siz[i]));
        an1=min(an1,c[i]);
    }
    f[now]=max(an,an1);
    return;
}
inline void dfs(int step)
{
    if(step==n+1)
    {
        klske();
        return;
    }
    con[step]=1;dfs(step+1);
    con[step]=0;dfs(step+1);
    return;
}
inline double getans(int zt)
{
    if(g[zt]>=0)return g[zt];
    g[zt]=f[zt];
    for(int i=1;i<=zt;i++)
    {
        if(i==zt)break;
        if((i&zt)==i)g[zt]=max(g[zt],min(getans(i),getans(i^zt)));
    }
    return g[zt];
}
signed main()
{
    freopen("desert.in","r",stdin);
    freopen("desert.out","w",stdout);
    scanf("%d",&n);
    memset(g,-1,sizeof(g));
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            dis[i][j]=sqrt((a[i]-a[j])*(a[i]-a[j])+(b[i]-b[j])*(b[i]-b[j]));
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)di[++len]=(edge){i,j,dis[i][j]};
    }
    sort(di+1,di+len+1);
    dfs(1);
    getans((1<<n)-1);
    printf("%.10lf\n",g[(1<<n)-1]);
    return 0;
}

T4 奇異函式

不會。