AmazingCounters.com

2017-3-5四校联考

szy学长出的,除了T3外都比较水 360/400。

 

T1.小猪划船

题目大意:六只猪要过河,三只大猪ABC,三只小猪abc,其中ABCa会划船,共一只船,每次可以载2个人,给出四只猪的划船耗时,每次运载花的时间是船上耗时最小的猪的耗时乘上船上猪的个数,小猪与其对应大猪不在一起时不能与其他大猪在一起,求所有猪到对岸的最小耗时。

思路:状态最多2^7(每只猪还有船的位置),随便建图最短路或者直接搜索或者构造都能通过该题。我写的O(2^21)。

#include<cstdio>
#include<algorithm>
using namespace std;
#define INF 0x3FFFFFFF
int g[130][130],t[4],a[6],b;
bool check()
{
    if(a[3]!=a[0]&&(a[3]==a[1]||a[3]==a[2]))return false;
    if(a[4]!=a[1]&&(a[4]==a[0]||a[4]==a[2]))return false;
    if(a[5]!=a[2]&&(a[5]==a[0]||a[5]==a[1]))return false;
    return true;
}
int hash()
{
    int r=0,i;
    for(i=0;i<6;++i)r|=(a[i]<<i);
    return r|(b<<6);
}
int main()
{
    freopen("boat.in","r",stdin);
    freopen("boat.out","w",stdout);    
    int i,j,k,x;
    for(i=0;i<4;++i)scanf("%d",&t[i]);
    for(i=0;i<1<<7;++i)for(j=0;j<1<<7;++j)if(i!=j)g[i][j]=INF;
    for(i=0;i<1<<7;++i)
    {
        for(j=0;j<6;++j)a[j]=bool(i&(1<<j));b=bool(i&(1<<6));
        if(!check())continue;
        for(j=0;j<4;++j)if(a[j]==b)
        {
            a[j]^=1;b^=1;
            if(check())x=hash(),g[i][x]=min(g[i][x],t[j]);
            a[j]^=1;b^=1;
            for(k=0;k<6;++k)if(j!=k&&a[k]==b)
            {
                if(j==3&&(k==1||k==2))continue;
                a[j]^=1;a[k]^=1;b^=1;
                if(check())x=hash(),g[i][x]=min(g[i][x],t[j]<<1);
                a[j]^=1;a[k]^=1;b^=1;
            }
        }
    }
    for(k=0;k<1<<7;++k)for(i=0;i<1<<7;++i)for(j=0;j<1<<7;++j)
        g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
    printf("%d",g[0][127]);
    fclose(stdin);fclose(stdout);return 0;
}

 

T2.小猪星球

题目大意:给出N个点M条边的图,边权可能为负数,可以一次把所有边的边权加上同一个数,求出1到n非负的最短路。(N<=100,M<=N(N+1)/2)

思路:以前的原题。二分加上多少,SPFA check。

#include<cstdio>
#include<cstring>
#define MN 100
#define ME 5050
#define INF 100000
struct edge{int nx,t,w;}e[ME*2+5];
int h[MN+5],rh[MN+5],en,u[MN+5],q[MN+5],qn,d[MN+5],z[MN+5];
void ins(int*h,int x,int y,int w){e[++en]=(edge){h[x],y,w};h[x]=en;}
int spfa(int x,int c)
{
    if(z[x])return 1;z[x]=1;
    for(int i=h[x];i;i=e[i].nx)if(u[e[i].t]&&d[x]+e[i].w+c<d[e[i].t])
        {d[e[i].t]=d[x]+e[i].w+c;if(spfa(e[i].t,c))return 1;}
    return z[x]=0;
}
bool check(int x)
{
    memset(d,42,sizeof(d));
    memset(z,d[1]=0,sizeof(z));
    return !spfa(1,x);
}
int main()
{
    freopen("planet.in","r",stdin);
    freopen("planet.out","w",stdout);
    int T,n,m,x,y,w,i,j,l,r,mid,ans;
    for(scanf("%d",&T);T--;)
    {
        scanf("%d%d",&n,&m);
        memset(h,en=0,sizeof(h));memset(rh,0,sizeof(rh));
        while(m--)
        {
            scanf("%d%d%d",&x,&y,&w);
            ins(h,x,y,w);ins(rh,y,x,w);
        }
        memset(u,0,sizeof(u));
        for(u[q[i=qn=0]=n]=1;i<=qn;++i)
            for(j=rh[q[i]];j;j=e[j].nx)if(!u[e[j].t])u[q[++qn]=e[j].t]=1;
        if(!u[1]){puts("-1");continue;}
        for(l=-INF,r=INF;l<=r;)
            if(check(mid=l+r>>1)&&d[n]>=0)r=mid-1,ans=d[n];
            else l=mid+1;
        printf("%d\n",ans);
    }
    fclose(stdin);fclose(stdout);return 0;
}

 

T3.小猪送货

题目大意:N个点,源到点i连一条流量pi的边,点i到汇连流量si的边,对所有i<j的i和j连流量c的单向边,求最大流。(N<=10000)

思路:CF原题。暴力60分。考虑转成最小割DP,由于点与点之间边的流量相同,可以用f[i][j]表示前i个点j个割S,i-j个割T的最小割,可以得到DP方程f[j]=min(f[j]+p[i]+c*j,f[j-1]+s[i]),复杂度O(n^2)。

暴力

#include<cstdio>
#include<cstring>
inline int read()
{
    int x=0;char c;
    while((c=getchar())<'0'||c>'9');
    for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 100
#define ME 5150
#define S MN+1
#define T MN+2
#define INF 0x7FFFFFFF
struct edge{int nx,t,w;}e[ME*2+5];
int h[MN+5],en=1,d[MN+5],q[MN+5],qn,c[MN+5];
long long ans;
inline void ins(int x,int y,int w)
{
    e[++en]=(edge){h[x],y,w};h[x]=en;
    e[++en]=(edge){h[y],x,0};h[y]=en;
}
bool bfs()
{
    int i,j;
    memset(d,0,sizeof(d));
    for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx)
        if(e[j].w&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1;
    return d[T];
}
int dfs(int x,int r)
{
    if(x==T)return r;
    int u=0,k;
    for(int&i=c[x];i;i=e[i].nx)if(e[i].w&&d[e[i].t]==d[x]+1)
    {
        k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w);
        u+=k;e[i].w-=k;e[i^1].w+=k;
        if(u==r)return r;
    }
    return d[x]=0,u;
}
int main()
{
    freopen("deliver.in","r",stdin);
    freopen("deliver.out","w",stdout);
    int n=read(),c=read(),i,j;
    for(i=1;i<=n;++i)ins(S,i,read());
    for(i=1;i<=n;++i)ins(i,T,read());
    for(i=1;i<n;++i)for(j=i+1;j<=n;++j)ins(i,j,c);
    while(bfs())ans+=dfs(S,INF);
    printf("%I64d",ans);
    fclose(stdin);fclose(stdout);return 0;
}
View Code

正解

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
inline int read()
{
    int x=0;char c;
    while((c=getchar())<'0'||c>'9');
    for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#include<vector>
#define MN 10000
#define INF (1LL<<60)
int a[MN+5],b[MN+5];
ll f[MN+5],ans=INF;
int main()
{
    int n=read(),c=read(),i,j;
    for(i=1;i<=n;++i)a[i]=read();
    for(i=1;i<=n;++i)b[i]=read();
    for(i=1;i<=n;++i)for(j=i;j>=0;--j)
        f[j]=min(j<i?f[j]+a[i]+(ll)c*j:INF,j?f[j-1]+b[i]:INF);
    for(i=0;i<=n;++i)ans=min(ans,f[i]);
    printf("%I64d",ans);
}

 

T4.小猪数数

题目大意:两个人玩游戏,一开始各有一个1,每次可以选择一个玩家,让他的数加上对方的数,给出N,M,问最少进行几轮游戏到达当前局面,不可能则输出-1。(N,M在long long范围内)

思路:若n>m,则n只能是由n-m加上m得到的,故可以用求gcd的辗转相除法处理,若求出的gcd不为1则输出-1,另外数据可能有0或负数,要特判……复杂度O(logN+logM)。

#include<cstdio>
#define ll long long
ll a,b,ans;
ll gcd(ll a,ll b){return b?(ans+=a/b,gcd(b,a%b)):a;}
int main()
{
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    scanf("%I64d%I64d",&a,&b);
    if(a<1||b<1||gcd(a,b)!=1)puts("-1");
    else printf("%I64d",ans-1);
    fclose(stdin);fclose(stdout);return 0;
}

 

posted on 2017-03-05 14:47  ditoly  阅读(247)  评论(0编辑  收藏  举报