2017-2018 ACM-ICPC German Collegiate Programming Contest (GCPC 2017)

题目传送门

题号 A B C D E F G H I J K L
状态  .  . Ο Ο Ø Ο Ο Ο

 

Ο:当场

Ø:已补

.  :  待补

 

A Drawing Borders

待补。

 

B Buildings

待补。

 

C Joyride

Code:kk

Thinking:kk

  题意游乐场有n个设施,有m条人行道,游乐设施会花费ti的时间和pi的钱,人行道需要花费t的时间,你需要用最少的钱恰好游玩x的时间,起点是1,终点是1,求最少的钱是多少4,并且1必须经过两次。

  这道题做的我心情复杂。。

  先说解法吧,其实很简单,$dp[i][t]$表示到第i个节点,已经使用了t的时间的最小花费,那么状态转移方程就是$dp[v][t]=min(do[u][t-tv]$,然后用队列存放状态,跑一个类似dijkstra堆优化的东西,只不过不用优先队列,特别注意1必须经过两次,所以一开始要判断一下一号节点的时间。

  是不是很简单呢。。然而为了写的方便,我把每个节点自己和自己都连了一个环,然后这样的边数就变成了3*n,而我链式前向星的结构体只开了两倍,显示wa9,连带着队友一起自闭了,其他好多题可以做的啊啊啊啊啊

 

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=2010;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll dp[maxn][maxn],p[maxn],maxx;
bool vis[maxn][maxn];
int n,m,x,t,head[maxn],tot;
int tim[maxn];
struct node{
    int u,t;
}st;
struct edge{
    int to,Next;
    ll w;
}a[maxn<<1];
inline void addv(int u,int v,int w){
    a[++tot].to=v;
    a[tot].w=w;
    a[tot].Next=head[u];
    head[u]=tot;
}
void bfs(){
    clr(dp,inf);
    if(tim[1]>=x)return;
    dp[1][tim[1]]=p[1];
    queue<node>q;
    q.push({1,tim[1]});
//    vis[1][tim[1]]=1;
    
    while(!q.empty())
    {
        st=q.front();
        q.pop();
        int u=st.u,ti=st.t;
    //    vis[u][ti]=0;
        if(st.t>x)continue;
        for(int i=head[u];i!=-1;i=a[i].Next)
        {
            int v=a[i].to;
            int cotime=a[i].w;
            if(cotime+ti+tim[v]>x)continue;
        //    printf("dbbug\n");
            if(dp[v][cotime+ti+tim[v]]>dp[u][ti]+p[v]){
            
                dp[v][cotime+ti+tim[v]]=dp[u][ti]+p[v];
            //    printf("%d  %d   %d\n",u,v,dp[v][cotime+ti+tim[v]]);
            //    if(!vis[v][cotime+ti+tim[v]]){
                    q.push({v,cotime+ti+tim[v]});
                //    vis[v][cotime+ti+tim[v]] = 1;
            //    }
            }
        }
    }
    maxx=min(maxx,dp[1][x]);
}
void pop()
{

            for(int i=1;i<=101001;i++) 
             for(int j=1;j<=101010;j++)
                 ;
}
int main(){
    while(cin>>x)
    {
    
        clr(head,-1),tot=0;
        scanf("%d%d%d",&n,&m,&t);
    
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d",&u,&v);
            if(u==v) pop();
            addv(u,v,t);
            addv(v,u,t);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d%lld",&tim[i],&p[i]);
            addv(i,i,0);
        }
        
        maxx=inf;
        bfs();
        if(maxx==inf)puts("It is a trap.");
        else printf("%lld\n",maxx);
    }
}
View Code

 

D.Pants On Fire

Thinking:zz

Code:zz

题意:先给n个谁比谁菜的关系,然后给m个谁比谁菜的关系,判断这m个关系是正确的、错误的还是不明确的。

题解:按n个关系建图,然后直接bfs暴力判断这m个关系是不是对的。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=110010;
char c[1010],d[1010],tmp[1010];
unordered_map<string,int>mp;
vector<int>ve[1010];
int v[550];
int main()
{
    int n,m,i,j,k,cnt,xx,yy,pos,si,qq,ans;
    while(~scanf("%d %d",&n,&m))
    {
        for(i = 0;i <= 1000;i++)
        {
            ve[i].clear();
        }
        cnt = 1;
        for(i = 0;i < n;i++)
        {
            scanf("%s",c);
            scanf("%s",tmp);
            scanf("%s",tmp);
            scanf("%s",tmp);
            scanf("%s",d);
            if(!mp[c])
            {
                mp[c] = cnt++;
            }
            if(!mp[d])
            {
                mp[d] = cnt++;
            }
            xx = mp[c];
            yy = mp[d];
            ve[yy].push_back(xx);
        }
        for(i = 0;i < m;i++)
        {
            scanf("%s",c);
            scanf("%s",tmp);
            scanf("%s",tmp);
            scanf("%s",tmp);
            scanf("%s",d);
            xx = mp[c];
            yy = mp[d];
            if(xx == 0 || yy == 0)
            {
                printf("Pants on Fire\n");
                continue;
            }
            else
            {
                queue<int>q;
                q.push(yy);
                memset(v,0,sizeof(v));
                v[yy] = 1;
                qq = 0;
                //printf("111\n");
                while(!q.empty())
                {
                    //printf("222\n");
                    pos = q.front();
                    q.pop();
                    //printf("2222  %d\n",pos);
                    si = ve[pos].size();
                    //printf("22222\n");
                    for(j = 0;j < si;j++)
                    {
                        //printf("333\n");
                        if(!v[ve[pos][j]])
                        {
                            v[ve[pos][j]] = 1;
                            if(xx == ve[pos][j])
                            {
                                qq = 1;
                                break;
                            }
                            //printf("  %d\n",ve[pos][j]);
                            q.push(ve[pos][j]);
                        }
                    }
                    if(qq)
                    {
                        break;
                    }
                }
                //printf("999\n");
                if(qq)
                {
                    ans = 1;
                }
                else
                {
                    //printf("111\n");
                    queue<int>qqq;
                    qqq.push(xx);
                    memset(v,0,sizeof(v));
                    v[xx] = 1;
                    qq = 0;
                    while(!qqq.empty())
                    {
                        pos = qqq.front();
                        qqq.pop();
                        si = ve[pos].size();
                        for(j = 0;j < si;j++)
                        {
                            if(!v[ve[pos][j]])
                            {
                                v[ve[pos][j]] = 1;
                                if(yy == ve[pos][j])
                                {
                                    //printf("5555\n");
                                    qq = 1;
                                    break;
                                }
                                qqq.push(ve[pos][j]);
                            }
                        }
                        if(qq)
                        {
                            break;
                        }
                    }
                    if(qq)
                    {
                        ans = 2;
                    }
                    else
                    {
                        ans = 3;
                    }
                }
            }
            if(ans == 1)
            {
                printf("Fact\n");
            }
            else if(ans == 2)
            {
                printf("Alternative Fact\n");
            }
            else
            {
                printf("Pants on Fire\n");
            }
        }
    }
}
View Code

 

E Perpetuum Mobile

待补

 

F Plug It In!

补题:kk

  这题看上去就很模板的样子。。比赛的时候没做真是太蠢了。。

  暴力枚举每个点,然后每次都重新跑二分图显然是会超时的,但是我们发现,如果新增加两个节点,就可以直接在原图的增广路径上来跑二分图,这样只需要对新增加的两个点跑就可以了,所以就把原图复制一下,(只需要复制girl数组就好)然后暴力枚举每个点。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1510;
vector<int >ve[maxn];
int girl[maxn],vis[maxn],match[maxn];
int n,m,k;
bool dfs(int x)
{
    for(auto v:ve[x])
    {
        if(vis[v])continue;
        vis[v]=1;
        if(girl[v]==0||dfs(girl[v])){
            girl[v]=x;
            return true;
        }
    }
    return false;
}
bool dfs_2(int x)
{
    for(auto v:ve[x])
    {
        if(vis[v])continue;
        vis[v]=1;
        if(match[v]==0||dfs_2(match[v])){
            match[v]=x;
            return true;
        }
    }
    return false;
}
int main(){
    while(cin>>m>>n>>k)
    {
        for(int i=1;i<=k;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            ve[u].push_back(v);
        }
        int ans=0;
        for(int i=1;i<=m;i++)
        {
            clr(vis,0);
            if(dfs(i))ans++;
        }
        int maxx=ans;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                match[j]=girl[j];
            }
            ve[1+m].clear(),ve[2+m].clear();
            for(int j=0;j<ve[i].size();j++)
            {
                
                ve[1+m].push_back(ve[i][j]);
                ve[2+m].push_back(ve[i][j]);
            }
            int tep=ans;
            for(int j=m+1;j<=m+2;j++)
            {
                clr(vis,0);
                if(dfs_2(j))tep++;
            }
            maxx=max(maxx,tep);
        }
        cout<<maxx<<endl;
    }
}
View Code

 

G Water Testing

皮克定理

皮克定理:指一个计算点阵中顶点在格点上的多边形面积公式,该公式可以表示为$2S=2a+b-2$,其中a表示多边形内部的点数,b表示多边形边界上的点数,S表示多边形的面积。

Code:zz

Thinking:zz

题意:按顺序给定一个凸包上的所有点,判断凸包内有多少个整数点(凸包上的点不算)。

题解:设凸包上的点数为a,凸包内的点数为b,凸包的面积为s,则s=b+a/2-1(皮克定理),这样一来算出s(用叉乘算)和a(用gcd算)就行了。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1010;
const ll inf=0x3f3f3f3f3f3f3f3f;
struct ss
{
    long long a,b;
}z[100010],zz;
inline long long Gcd(long long n,long long m)
{
    long long r;
    while(n % m != 0)
    {
        r = n % m;
        n = m;
        m = r;
    }
    return m;
}
int main(){
    int n,i,j;
    long long s,a,xx,yy,tmp,ans;
    while(~scanf("%d",&n))
    {
        for(i = 1;i <= n;i++)
        {
            scanf("%lld %lld",&z[i].a,&z[i].b);
        }
        z[n + 1] = z[1];
        s = 0;
        for(i = 1;i <= n;i++)
        {
            s += z[i].a * z[i + 1].b - z[i].b * z[i + 1].a;
        }
        //printf("  s = %lld\n",s);
        if(s <= 0)
        {
            for(i = 1;i <= n / 2;i++)
            {
                zz = z[i];
                z[i] = z[n - i + 1];
                z[n - i + 1] = zz;
            }
            z[n + 1] = z[1];
            s = 0;
            for(i = 1;i <= n;i++)
            {
                s += z[i].a * z[i + 1].b - z[i].b * z[i + 1].a;
            }
        }
        a = 0;
        for(i = 1;i <= n;i++)
        {
            xx = z[i].a - z[i + 1].a;
            yy = z[i].b - z[i + 1].b;
            if(xx < 0)
            {
                xx = -xx;
            }
            if(yy < 0)
            {
                yy = -yy;
            }
            if(xx == 0 || yy == 0)
            {
                xx = max(xx,yy);
                a += xx;
            }
            else
            {
                tmp = Gcd(xx,yy);
                a += tmp;
            }
        }
        ans = s + 2 - a;
        printf("%lld\n",ans / 2);
    }
}
View Code

 

 

H Ratatöskr

待补

 

I Überwatch

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=110010;
int n,m,x[301000],g[301000],dp[301000];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&x[i]);
    for(int i=m+1;i<=n;i++)
    {
        dp[i]=max(dp[i-1],g[i-m]+x[i]);
        g[i]=max(g[i-1],dp[i]);
    }
    printf("%d\n",g[n]);
}
View Code

 

J Word Clock

 

K You Are Fired!

Code:zz

Thinking:zz

题意:解雇最少的员工,使得至少节省d元,但最多解雇k个员工。

题解:按工资从大到小排序,优先解雇工资高的。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=110010;
struct s
{
    char c[20];
    long long v;
}z[10010];
inline bool comp(s a,s b)
{
    return a.v > b.v;
}
int main(void)
{
    int n,k,i;
    long long d,zz;
    while(~scanf("%d %lld %d",&n,&d,&k))
    {
        for(i = 0;i < n;i++)
        {
            scanf("%s %lld",z[i].c,&z[i].v);
        }
        sort(z,z + n,comp);
        zz = 0;
        int j;
        for(i = 0;i < k;i++)
        {
            zz += z[i].v;
            if(zz >= d)
            {
                break;
            }
        }
        if(zz >= d)
        {
            printf("%d\n",i + 1);
            for(j = 0;j <= i;j++)
            {
                printf("%s, YOU ARE FIRED!\n",z[j].c);
            }
        }
        else
        {
            printf("impossible\n");
        }
    }
}
View Code

 


赛后总结:

  kk:今天被C题搞死了,想出ac想法的时候只有一点点人ac这道题,真正ac的时候却已经是四个小时后了,而且是一个小bug。不但自己没做其他题,还拖累了队友的做题速度,其实还有其他很多可做的题目,一定要多看题!以后遇到这样的情况,要勇于换人重写代码。

  zz:今天就做了三道水题,读了题面就知道怎么做了,后来和队友一起搞C,其它题基本没怎么想。

 

posted @ 2019-03-06 22:26  光芒万丈小太阳  阅读(308)  评论(0编辑  收藏  举报