GCPC2017 部分题题解

C joyride

It is another wonderful sunny day in July – and you decided to spend your day together with your little daughter Joy. Since she really likes the fairy-park in the next town, you decided to go there for the day. Your wife (unfortunately she has to work) agreed to drive you to the park and pick you up again. Alas, she is very picky about being on time, so she told you exactly when she will be at the park’s front entrance to pick you up and you have to be there at exactly that time. You clearly also don’t want to wait outside, since this would make your little daughter sad – she could have spent more time in the park! Now you have to plan your stay at the park. You know when you will arrive and when you will have to depart. The park consists of several rides, interconnected by small pavements. The entry into the park is free, but you have to pay for every use of every ride in the park. Since it is Joy’s favorite park, you already know how long using each ride takes and how much each ride costs. When walking through the park, you obviously must not skip a ride when walking along it (even if Joy has already used it), or else Joy would be very sad. Since Joy likes the park very much, she will gladly use rides more than once. Walking between two rides takes a given amount of time. Since you are a provident parent you want to spend as little as possible when being at the park. Can you compute how much is absolutely necessary?

这是七月里另一个阳光明媚的日子,你决定和你的女儿joy共度时光,因为她特别喜欢下一个小镇子里的仙女公园(魔仙堡),你打算去那待一天,你的妻子(不幸的是她得工作),同意开车载你去公园并把你接回来。哎呀!她对时间卡得非常紧,所以她告诉你她会到达公园入口的准确时间,到那个点你得正正好好在那里。你也很明白自己不想在外边等着,因为这会让你的女儿很伤心:她本可以在公园多待会的!现在你得去计划一下你在公园的行程。你知道你什么时候来和你什么时候得走。这个公园包含几个游乐设施,它们内部也有一些小道连接着,公园没有门票,但是你玩一个项目就要花钱。因为这是joy最喜欢的公园,你早就知道每个游乐设施要花多久时间玩完,门票是多少。当在公园里边穿行的时候,很明显你不能略过你经过的游乐设施,因为这会让joy很不开心。因为joy很喜欢这个公园,所以她很乐意把一个游乐设施玩很多次。在两个设施间跋涉时也会消耗一些时间。因为你是一个深谋远虑的家长,你想着在公园里花尽量少的钱,你能计算出有多少票子是必须花的吗?

对本题图进行分析,可以得出对于每种游乐设施有两种不同的方案:玩一次去玩下一个或者再玩多次。那么自然就可以引申出两种方案:记忆化搜索和分层图最短路

如何理解记忆化搜索解决本题呢?

可以假定dp数组的第一维是所在位置,第二维是所花时间,然后最终状态是位于1号位置,且所花时间为remaining。然后用dfs遍历整个图,对于每一个结点都分成两种不同方案分别解决:

  1. 沿着图继续向前搜索
  2. 重复对当前点进行递归

  最终把所有递归的子答案累加到初始状态上就是本题答案。 

import java.math.*;
import java.io.*;
import java.text.*;
import java.util.*;
public class Main
{
    static int connections[][]=new int[3250][3250];
    static int dp[][]=new int[3250][3250];
    static int MoneyCost[]=new int[3250];
    static int TimeCost[]=new int[3250];
    static final int INF=1000000007;
    static int ride,pavement,cost;
    public static int dfs(int CurrentLoc,int CurrentTime,int Tlimit,int Rlimit)
    {
        if(CurrentTime>Tlimit)
            return INF;
        else if(CurrentLoc==1&&CurrentTime==Tlimit)这是初始状态
            return 0;
        else if(dp[CurrentLoc][CurrentTime]!=-1)
            return dp[CurrentLoc][CurrentTime];
        else
        {
            dp[CurrentLoc][CurrentTime]=INF;
            for(int i=1;i<=Rlimit;i++)
            {
                if(connections[CurrentLoc][i]>0)
                {
                    dp[CurrentLoc][CurrentTime]=
                    Math.min(dp[CurrentLoc][CurrentTime],dfs(i,CurrentTime+cost+TimeCost[i],Tlimit,Rlimit)+MoneyCost[i]);
                }
            }
            dp[CurrentLoc][CurrentTime]=
            Math.min(dp[CurrentLoc][CurrentTime],dfs(CurrentLoc, CurrentTime+TimeCost[CurrentLoc],Tlimit, Rlimit)+MoneyCost[CurrentLoc]);
            return dp[CurrentLoc][CurrentTime];
        }
    }
    public static void main(String args[])throws IOException
    {
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        String tmp=bf.readLine();
        int remaining=Integer.parseInt(tmp);
        tmp=bf.readLine();
        String tmps[]=tmp.trim().split(" ");
        ride=Integer.parseInt(tmps[0]);pavement=Integer.parseInt(tmps[1]);cost=Integer.parseInt(tmps[2]);
        for(int i=1,tmp3,tmp4;i<=pavement+ride;i++)
        {
            tmp=bf.readLine();
            String tmps2[]=tmp.trim().split(" ");
            tmp3=Integer.parseInt(tmps2[0]);tmp4=Integer.parseInt(tmps2[1]);
            if(i<=pavement)
            {
                connections[tmp3][tmp4]=1;
                connections[tmp4][tmp3]=1;
            }
            else
            {
                TimeCost[i-pavement]=tmp3;
                MoneyCost[i-pavement]=tmp4;
            }
        }
        for(int i=0;i<=1250;i++)
            for(int j=0;j<=1250;j++)
                dp[i][j]=-1;
        int ans=dfs(1,TimeCost[1],remaining,ride)+MoneyCost[1];
        if(ans>=INF)
            System.out.println("It is a trap.");
        else
            System.out.println(ans);
        bf.close();
    }
}

如果用分层图最短路解决的话,思路和上述的记忆化搜索差不多,利用一个二维数组存储不同位置,不同时间的结果即可。

import java.math.*;
import java.io.*;
import java.text.*;
import java.util.*;
 
import sun.misc.ObjectInputFilter.Status;
public class Main
{
    public static class Node
    {
        int next,to;
        public Node(int a,int b)
        {
            this.next=a;
            this.to=b;
        }
    }
    public static class Status
    {
        int CurTime,CurLoc;
        public Status(int a,int b)
        {
            this.CurTime=b;
            this.CurLoc=a;
        }
    }
    static final int INF=1000000007;
    static Node nodes[]=new Node[25000];
    static int heads[]=new int[25000];
    static int MoneyCost[]=new int[25000];
    static int TimeCost[]=new int[25000];
    static int distances[][]=new int[2500][2500];
    static boolean inquiry[][]=new boolean[2500][2500];//标记数组也要开二维
    static int cnt=0,remaining,ride,pavement,cross;
    public static void construction(int from,int to)
    {
        nodes[cnt]=new Node(heads[from],to);
        heads[from]=cnt++;
    }
    public static void spfa(int threshold)
    {
        for(int i=0;i<=1230;i++)
            for(int j=0;j<=1230;j++)
            {
                distances[i][j]=INF;
                inquiry[i][j]=false;
            }
        Queue<Status> q=new LinkedList<Status>();
        inquiry[threshold][TimeCost[1]]=true;
        distances[threshold][TimeCost[threshold]]=MoneyCost[1];
        Status tmp6=new Status(threshold,TimeCost[threshold]);
        q.add(tmp6);
        while(q.isEmpty()==false)
        { 
            Status cur=q.poll();
            //System.out.println(cur.CurTime);
            inquiry[cur.CurLoc][cur.CurTime]=false;
            int nxttime=(cur.CurTime+TimeCost[cur.CurLoc]);
            //System.out.println(cur.CurLoc+" "+nxttime+" "+remaining);
            if(nxttime<=remaining)
            {
               if(distances[cur.CurLoc][cur.CurTime]+MoneyCost[cur.CurLoc]<distances[cur.CurLoc][nxttime])
               {
                    distances[cur.CurLoc][nxttime]=distances[cur.CurLoc][cur.CurTime]+MoneyCost[cur.CurLoc];
                    if(inquiry[cur.CurLoc][nxttime]==false)
                    {
                        Status tmp7=new Status(cur.CurLoc,nxttime);
                        q.add(tmp7);
                        inquiry[cur.CurLoc][nxttime]=true;
                    }
               }
            }
            
            for(int i=heads[cur.CurLoc];i!=-1;i=nodes[i].next)
            {
                int nxt=nodes[i].to;
                int nxtTime=cur.CurTime+cross+TimeCost[nxt];
                if(nxtTime>remaining)
                    continue;
                else
                {
                    if(distances[nxt][nxtTime]>distances[cur.CurLoc][cur.CurTime]+MoneyCost[nxt])
                    {
                        distances[nxt][nxtTime]=distances[cur.CurLoc][cur.CurTime]+MoneyCost[nxt];
                        if(!inquiry[nxt][nxtTime])
                        {
                            Status tmp9=new Status(nxt,nxtTime);
                            q.add(tmp9);
                            inquiry[nxt][nxtTime]=true;
                        }
                    }
                }
            }
        }
    }
    public static void main(String args[])throws IOException
    {
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        String tmp=bf.readLine();
        remaining=Integer.parseInt(tmp);
        tmp=bf.readLine();
        String tmps[]=tmp.trim().split(" ");
        ride=Integer.parseInt(tmps[0]);
        pavement=Integer.parseInt(tmps[1]);
        cross=Integer.parseInt(tmps[2]);
        for(int i=1;i<=2500;i++)
            heads[i]=-1;
        for(int i=1,a,b;i<=pavement+ride;i++)
        {
            tmp=bf.readLine();
            String tmps2[]=tmp.trim().split(" ");
            a=Integer.parseInt(tmps2[0]);b=Integer.parseInt(tmps2[1]);
            if(i>pavement)
            {
                TimeCost[i-pavement]=a;
                MoneyCost[i-pavement]=b;
            }
            else
            {
                construction(a,b);
                construction(b,a);
            }
        }
        spfa(1);
        // for(int i=1;i<=6;i++)
        //     System.out.println(distances[1][i]);
        if(distances[1][remaining]>=INF)
            System.out.println("It is a trap.");
        else
            System.out.println(distances[1][remaining]);
        bf.close();
    }
 
}

E plug it in!

Adam just moved into his new apartment and simply placed everything into it at random. This means in particular that he did not put any effort into placing his electronics in a way that each one can have its own electric socket.
Since the cables of his devices have limited reach, not every device can be plugged into every socket without moving it first. As he wants to use as many electronic devices as possible right away without moving stuff around, he now tries to figure out which device to plug into which socket. Luckily the previous owner left behind a plugbar which turns one electric socket into 3. 
Can you help Adam figure out how many devices he can power in total?

Adam刚刚搬入他的新公寓,把所有东西都乱放一气。 这特别代表它没把各个电子设备整理到每个电子设备都有插头的情况。因为这个电子设备的电缆有功率限制,不是每个设备都可以找到各自的插口,在他不愿折腾并且想找到尽多的插口的状态下,他试图找到一种哪个插头匹配哪个接口的方案。幸运的是房东给了他一个插排,这使得他能把一个插头当三个用,你能帮帮Adam找到最多的可以接入电路的电器数吗?

 

典型的二分图匹配,但是这个对某个点进行了增强使其可以匹配三个点,所以略有不同。至于如何实现这样一个点当三个用的设定呢?如果强行枚举每个点扩容三倍的情况,那么极有可能超时。

所以换个思路,不是设备匹配插头,而是插头匹配设备。当一个插头匹配到了三个设备,就可以完成所谓的三倍扩容。至于实现就直接对某个点多进行两次二分图匹配即可。(由于额外附加不能超过两个,所以当答案更新到2的时候直接跳出循环即可)

import java.math.*;
import java.io.*;
import java.text.*;
import java.util.*; 
public class Main
{
    public static class Node
    {
        int next,to;
        public Node(int a,int b)
        {
            this.next=a;
            this.to=b;
        }
    }
    static Node nodes[]=new Node[125000];
    static int cnt=0;
    static int heads[]=new int[15000];
    static int socket,device,connection;
    public static void construction(int from,int to)
    {
        nodes[cnt]=new Node(heads[from],to);
        heads[from]=cnt++;
    }
    static int matches[]=new int[125000];
    static int secmatches[]=new int[125000];
    static boolean inquiry[]=new boolean[125000];
    public static Boolean Matching(int current)
    {
        for(int i=heads[current];i!=-1;i=nodes[i].next)
        {
            int nxt=nodes[i].to;
            if(inquiry[nxt]==false)
            {
                inquiry[nxt]=true;
                if(matches[nxt]==0||Matching(matches[nxt])==true)
                {
                    matches[nxt]=current;
                    return true;
                }
            }
        }
        return false;
    }
    public static void main(String args[])throws IOException
    {
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        String tmp=bf.readLine();
        String tmps[]=tmp.trim().split(" ");
        socket=Integer.parseInt(tmps[0]);
        device=Integer.parseInt(tmps[1]);
        connection=Integer.parseInt(tmps[2]);
        for(int i=1;i<=socket+device;i++)
            heads[i]=-1;
        for(int i=1,a,b;i<=connection;i++)
        {
            tmp=bf.readLine();
            String tmps2[]=tmp.trim().split(" ");
            a=Integer.parseInt(tmps2[0]);
            b=Integer.parseInt(tmps2[1]);
            construction(a,b);
        }
        long ans=0;
        for(int i=1;i<=socket;i++)//一对一匹配
        {
            for(int j=1;j<=device;j++)
                inquiry[j]=false;
            if(Matching(i)==true)
                ans++;
        }
        for(int y=1;y<=device;y++)
                secmatches[y]=matches[y];复制匹配结果
        //System.out.println(ans);
        long finans=0,tmpans=0;
        for(int i=1;i<=socket;i++)
        {
            for(int y=1;y<=device;y++)
                matches[y]=secmatches[y];
            tmpans=0;
            for(int j=1;j<=2;j++)//针对某个点多进行两次匹配。
            {
                for(int k=1;k<=device;k++)
                    inquiry[k]=false;
                if(Matching(i)==true)
                    tmpans++;
            }
            finans=Math.max(finans,tmpans);
            if(finans==2)
                break;
        }
        System.out.println(finans+ans);
        bf.close();
    }
}

 

I uberwatch

The lectures are over, the assignments complete and even those pesky teaching assistants have nothing left to criticize about your coding project. Time to play some video games! As always, your procrastinating self has perfect timing: Cold Weather Entertainment just released Überwatch, a competitive first person video game! Sadly, you aren’t very good at these kind of games. However, Überwatch offers more than just skill based gameplay. In Überwatch you can defeat all opponents in view with a single button press using your ultimate attack. The drawback of this attack is that it has to charge over time before it is ready to use. When it is fully charged you can use it at any time of your choosing. After its use it immediately begins to charge again. With this knowledge you quickly decide on a strategy: • Hide from your opponents and wait for your ultimate attack to charge. • Wait for the right moment. • Defeat all opponents in view with your ultimate attack. • Repeat. After the game your teammates congratulate you on your substantial contribution. But you wonder: How many opponents could you have defeated with optimal timing? The game is observed over n time slices. The ultimate attack is initially not charged and requires m time slices to charge. This first possible use of the ultimate attack is therefore in the (m+1)-th time slice. If the ultimate attack is used in the i-th time slice, it immediately begins charging again and is ready to be fired in the (i + m)-th time slice

宣讲会结束了,任务完成。即使就连烦人的助教也对你的编程项目无刺可挑。是时候去打场游戏了!一如既往,有拖延症的你对时机把握的不错:冷空气公司刚刚发行了一款Uberwatch,一个有挑战性的第一人称游戏。不幸的是,你不是很擅长这类游戏。但是,uberwatch提供了不止一种的基于游戏操作的小技巧。在uberwatch里你可以通过按下一个键发动无限攻击就可以击垮所有敌人,但是这种攻击的缺点就是有CD时间。在CD时间结束后你就可以在任何时候发动技能。在技能使用结束后立即进行冷却。凭着这个小知识点你可以快速想出一个妙招:躲着敌人,直到无限攻击可以使用,等待恰当时机,重复如上。

在游戏结束后,你的队友都来赞赏你在刚刚的游戏中巨大的贡献值。但是你想知道,凭借着最佳的时机选择,你能最多清掉多少敌人?这个游戏可以看做N个时间段,无限攻击在一开始的时候并没有冷却好,需要M段时间去冷却。第一次CD时间结束是M+1个时间段。如果第I个时间段使用了无限攻击,它立刻开始进入CD时间并在第I+M的时刻再次解除CD

 

显然需要用到DP,针对此题有两种不同的方案:

第一个是宏观枚举。通过分析可以发现本题的状态虽然是断断续续的,但是总体上每一个使用技能的状态都是由第i-m个状态转移而来的,所以可以直接得出方程:DP【i】=max(DP[i-m],DP[i])

第二个是微观枚举。就是对冷却时间进行逐一分析,这样较为繁琐。

设第一维是时刻,第二维是已冷却时间,如果对冷却时间段进行分析,那么状态转移方程就会是DP[i][j]=max(dp[i][j],dp[i-1][j-1]),因为可能会有多种不同的方案共同到达dp[i][j]这个状态,所以在传递直接相关的状态时(前一秒冷却时间)也要检查一下最优性。

然后就是对发动技能的时候进行分析:分为开火或不开火两种。

如果开火,那么就会有这样的状态转移方式:dp[i][1]=max(dp[i][1],dp[i-1][interval]+enemies[i]),意思是当前开火并取得杀敌数的状态和已有的状态比哪个更优。

如果不开火,那么就会有这样的状态转移方式:dp[i][interval]=max(dp[i][interval],dp[i-1][interval]),仍然是在比对直接相关的状态和当前已有状态哪个更优。

import java.math.*;
import java.io.*;
import java.text.*;
import java.util.*;
public class Main
{
    static int enemies[]=new int[350000];
    static int dp[][]=new int[300900][12];
    public static void main(String args[])throws IOException
    {
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        String tmp=bf.readLine();
        String tmps[]=tmp.trim().split(" ");
        int n=Integer.parseInt(tmps[0]),interval=Integer.parseInt(tmps[1]);
        tmp=bf.readLine();
        String tmps2[]=tmp.trim().split(" ");
        for(int i=1;i<=n;i++)
            enemies[i]=Integer.parseInt(tmps2[i-1]);
        for(int i=interval+1;i<=n;i++)
        {
            for(int j=1;j<=interval;j++)
                dp[i][j]=Math.max(dp[i][j],dp[i-1][j-1]);
            dp[i][interval]=Math.max(dp[i][interval],dp[i-1][interval]);
            dp[i][1]=Math.max(dp[i][1],dp[i-1][interval]+enemies[i]);
        }
        int ans=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=interval;j++)
                ans=Math.max(dp[i][j],ans);
        System.out.println(ans);
        bf.close();
    }
 
}

C pants on fire

本题属于闭包传递,直接用改进的floyd算法解出即可。

import java.math.*;
import java.io.*;
import java.text.*;
import java.util.*;
public class Main
{
    static int value[]=new int[20000000];
    static int cnt=0;
    static boolean dis[][]=new boolean[500][500];
    static final int mod=19991126;
    public static void floyd(int limit)
    {
        for(int k=1;k<=limit;k++)
            for(int i=1;i<=limit;i++)
                for(int j=1;j<=limit;j++)
                {
                    dis[i][j]=(dis[i][j])||((dis[i][k])&&(dis[k][j]));
                }
    }
    public static void main(String args[])throws IOException
    {
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        String tmp=bf.readLine();
        String tmps[]=tmp.trim().split(" ");
        int n=Integer.parseInt(tmps[0]),m=Integer.parseInt(tmps[1]);
        cnt=0;
        for(int i=1;i<=n;i++)
        {
            tmp=bf.readLine();
            String tmps2[]=tmp.trim().split(" ");
            int res1=((tmps2[0].hashCode()%mod+mod)%mod),res2=(tmps2[4].hashCode()%mod+mod)%mod;
            if(value[res1]==0)
                value[res1]=++cnt;
            if(value[res2]==0)
                value[res2]=++cnt;
            dis[value[res1]][value[res2]]=true;
        }
        floyd(cnt);
        for(int i=1;i<=m;i++)
        {
            tmp=bf.readLine();
            String tmps3[]=tmp.trim().split(" ");
            int res1=(tmps3[0].hashCode()%mod+mod)%mod,res2=(tmps3[4].hashCode()%mod+mod)%mod;
            if(dis[value[res1]][value[res2]])
                System.out.println("Fact");
            else if(dis[value[res2]][value[res1]])
                System.out.println("Alternative Fact");
            else
                System.out.println("Pants on Fire");
        }
        bf.close();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值