征服困难,就是荣耀 jzoj 2017.8.16 B组

第一题

交换【推荐】 (Standard IO)

Description

  给定1到N的一个排列,再给定一些允许的交换方法,要求用最少的交换次数把该排列变为1,2,3,,,N。

Input

  第一行包含两个整数N(1<=N<=12)和M(1<=M<=N*(N-1)/2),表示序列的长度以及允许的交换方案。
  第二行输入1到N个初始排列情况。
  接下来M行,每行两个整数A和B描述一个允许的交换方案,表示允许把当前排列中的第A个数和第B个数进行交换,保证A和B不相同。
  输入保证一定存在解。

Output

  输出一个整数表示最少需要的交换次数。

Sample Input

输入1:
2 1
2 1
1 2

输入2:
3 2
2 1 3
1 3
2 3

输入3:
5 5
1 2 3 4 5
1 5
2 5
1 4
1 1
3 5

Sample Output

输出1:
1

输出2:
3

输出3:
0

题解:一开始用单向dfs,10分。
开始的起始状态我们是知道的,最终转移到的状态我们也是知道的,而且每种操作的改变对于正着做和反着做都是相同的,所以应该是双向bfs加上哈希表判重。

不过,我们如何记录它的状态?
开应该12维的布尔数组是不太科学的,因为N很小只用12,而且1213=106993205379072 这个数是可以用long long存的下的。那么我们就将每一个序列看成应该12进制的数,再将它转换成为应该10进制数。这样,我们就可以将每一个序列变成一个数。

ps:今天这题是我第一次用哈希表,然后我用数组模拟了队列

代码:

#include<iostream>
#include <cstdio>
#include <cstring>
#define LL long long
#define MOD 1000007
using namespace std;
long long ha[MOD],da1[3*MOD],date[3*MOD],z[15],s;
int g[MOD],n,m,l,now,a[15],t[15],cz[150][2],f[15];
bool bz[MOD];
int hash(LL x)
{
    int k=x%MOD;
    while(ha[k]&&ha[k]!=x)
    {
        k=(k+1)%MOD;
    }
    return  k;
}
long long zz()
{
    LL sum=0;
    for(int i=1;i<=n;i++)
    {
        sum+=t[i]*z[n-i];
    }
    return sum;
}

void aa(LL x)
{
    for(int i=1;i<=n;i++)
    {
        f[i]=x/z[n-i];
        x=x%z[n-i];
    }
}
int main()
{
    z[0]=1;
    for(int i=1;i<=13;i++)
        z[i]=z[i-1]*12;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];t[i]=i-1;
        a[i]--;
    }
    for(int i=1;i<=m;i++)
    {
        cin>>cz[i][0]>>cz[i][1];
    }

    da1[1]=zz();
    l=hash(da1[1]);
    ha[l]=da1[1];
    bz[l]=1;
    memcpy(t,a,sizeof(t));
    date[1]=zz();
    l=hash(date[1]);
    ha[l]=date[1];
    if(da1[1]==date[1])
    {
        printf("0\n");//相同直接输出0嘛 
        return 0;
    }

    int i=0,j=1,p=0,q=1;
    while((i<j)&&(p<q))
    {
        i++;
        aa(date[i]);
        now=g[hash(date[i])];
        for(int k=1;k<=m;k++)
        {
            memcpy(t,f,sizeof(t));
            l=t[cz[k][0]];
            t[cz[k][0]]=t[cz[k][1]];
            t[cz[k][1]]=l;
            s=zz();
            l=hash(s);
            if(ha[l]==s)
            {
                if(bz[l])
                {
                    cout<<g[l]+now+1;
                    return 0;
                }
            }
            else 
            {
                j++;
                date[j]=s;
                ha[l]=s;
                bz[l]=0;
                g[l]=now+1;
            }
        }

        p++;
        aa(da1[p]);
        now=g[hash(da1[p])];
        for(int k=1;k<=m;k++)
        {
            memcpy(t,f,sizeof(t));
            l=t[cz[k][0]];
            t[cz[k][0]]=t[cz[k][1]];
            t[cz[k][1]]=l;
            s=zz();
            l=hash(s);
            if(ha[l]==s)
            {
                if(!bz[l])
                {
                    cout<<g[l]+now+1;
                    return 0;
                }
            }
            else 
            {
                q++;
                da1[q]=s;
                ha[l]=s;
                bz[l]=1;
                g[l]=now+1;
            }
        }

    }
    return 0;
}

第二题

送披萨 (Standard IO)
Time Limits: 3000 ms Memory Limits: 256000 KB Detailed Limits
Goto ProblemSet

Description

  Alice刚刚收到一份送披萨的工作,每天工作前,他会收到一份送披萨的清单,必须按照上面的顺序依次送。
  城区被分为R*C块区域,行编号为1到R,列编号1到C,对于每个单元格,可以向左或向右移到相邻的单元格中,只有在第一列和第C列时才允许向上和向下移动。
  披萨店在坐上角(1,1)处,这里也是Alice出发的地方,Alice把每天需要送的披萨都带上,这样他就没有回到店里去取。
  进入每个单元格都需要花费一定的时间。
  编程计算Alice送完所有披萨最少需要花费多少时间。

Input

  第一行包含两个整数R和C(1<=R<=2000,1<=C<=200)。
  接下来R行,每行C个整数,表示进入每个单于格需要花费的时间,每个数都在0到5000之间。
  下一行输入一个整数D(1<=D<=200000),表示某天需要送的披萨数量。
  接下来D行,每行包含两个整数A和B(1<=A<=R,1<=B<=C),表示送披萨的位置,必须按照输入顺序依次去送,D个地址没有相同的。

Output

  输出最少需要花费的时间。

Sample Input

输入1:
3 3
1 8 2
2 3 2
1 0 1
3
1 3
3 3
2 2

输入2:
2 5
0 0 0 0 0
1 4 2 3 2
4
1 5
2 2
2 5
2 1

Sample Output

输出1:
17

输出2:
9

Data Constraint

Hint

【样例解释】
  样例1中,可以选择以下路线:(1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (2, 3), (1, 3), (2, 3), (3, 3), (2, 3),(2, 2),粗体的部分为送披萨的位置,总时间为1+2+1+0+1+2+2+2+1+2+3=17

【数据范围】
  70%的数据满足R<=250

第三题

买玩具 (Standard IO)
Time Limits: 3000 ms Memory Limits: 262144 KB Detailed Limits Special Judge
Goto ProblemSet

Description

  N个小朋友都很喜欢玩具,一开始每个人都没有玩具,但他们可以去买,买玩具有规定:必须两个小朋友一起去买,而且必须买2个玩具,两人各出一半钱,但玩具归谁就不一定了,要用石头剪刀布决定,赢者得2个,平各得1个,输者得0个。
经过若干次购买,每个人手里都有若干个玩具,但由于小朋友记性不好,只能回忆起M次购买是哪两个个小朋友去的,其他都不记得了。
请你帮忙输出一种可能的购买情况。

Input

  第一行包含两个整数N和M(1<=N<=100,0<=M<=1000),表示小朋友数量,以及能够回忆起来的购买数量,小朋友编号1到N。
第二行包含N个整数,表示每个小朋友手中玩具的数量。
接下来M行,每行包含两个整数,表示回忆起来的参与这次购买的小朋友的编号。

Output

  输出一共购买的次数X。
接下来X行,每行描述一次购买的情况,由三个整数a,b,c组成,a,b表示参与这次购买的小朋友,c只能取0,1或2,表示a在这次购买中得到的玩具数量。
注意:保证购买次数最多只有1000次,并且保证有解,但不是唯一的,输出任意一个即可。

Sample Input

输入1:
2 3
5 1
1 2
1 2
1 2

输入2:
4 3
5 3 1 1
1 3
2 3
4 1

输入3:
5 0
3 0 2 4 1

Sample Output

输出1:
3
1 2 1
1 2 2
1 2 2

输出2:
5
1 3 1
2 3 2
4 1 0
2 4 1
1 3 2

输出3:
5
1 2 2
1 3 1
4 2 2
3 4 0
3 5 1

Data Constraint

Hint

【样例解释】
样例1中,有2个小朋友,编号1和2,最终1号小朋友有5个玩具,2号有1个。
第1次购买,2人各得1个玩具,第2、3次,都是1号小朋友得2个玩具,2号没有得到玩具。

题解:dalao说用网络流,本蒟蒻不会。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值