URAL 1495. One-two, One-two 2

dp[ i ][ j ] 表示 长度为 i 的 对 n 取余为 j (若 j == 0 , j = n) 的最优解。

mod[1][ i ] 表示1 * (10)^(i-1) 对 n 取余的余数,若 (mod[1][ i ] == 0 ,则 mod[ 1 ][ i ] = n)。mod[2][ i ]同理。

状态转移方程 dp[ i ][ j ] = min(dp[ i-1 ][j - mod[1][ i- 1]] + 1*(10)^(i-1) , dp[ i-1 ][j - mod[2][ i- 1]] + 2*(10)^(i-1) )。

状态转移方程很像背包,但是若按背包那样去求解,最坏的时间复杂度会到 30*100W。应该会TLE,我没有去试。。

但是如果从 长度为 i 的状态 推出 长度为 i+1 的状态,即用BFS的方式从前往后推,时间复杂度很降低很多,但是依然TLE了。

原因在于无法确定推出的状态是否为最优解,因为 dp[i+1][ j ] 会从dp[ i ][ k ]推出多种方案。使得许多无用的状态都被计算了一次。

好吧,现在已经找到了剪枝的关键点,即剪掉这些无用的状态。

acc[ i ][ j ] 记录已经出现的解中最优的一个。从而使得加入BFS队列中的方案越来越靠近最优解,即最后一个加入队列的关于 dp[ i ][ j ]的解即为此种状态下的最优解。


还有一个问题就是最终的答案的值可能是个大数,但是因为只有1 和 2 ,所以可以用二进制来解决。

另外,int 的 30*100W的数组貌似会超内存,所以注意用滚动数组优化。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <stack>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-6)
#define LL long long int
#define ULL unsigned long long int
#define _LL __int64
#define _INF 0x3f3f3f3f
#define INF 40000000
#define Mod 1000000007

using namespace std;

int mod[3][33];

struct N
{
    int ans,num;
} acc[2][1000010];

int ans[2][1000010];

struct Q
{
    int ans,num,acc;
};

void Output(int n,int ans)
{
    int temp = acc[ans%2][n].num;

    int num[31],top = 0;

    while(ans >= 1)
    {
        num[top++] = temp%2;
        temp /= 2;
        ans--;
    }

    for(top--; top >= 0; top--)
    {
        printf("%d",num[top]+1);
    }
    puts("");

}

void bfs(int n)
{
    memset(acc,0,sizeof(acc));
    memset(ans,0,sizeof(ans));

    queue<Q> q;

    Q f,t;

    f.acc = mod[2][1];
    f.ans = 1;
    f.num = 1;

    q.push(f);

    ans[1][mod[2][1]]++;
    acc[1][mod[2][1]].ans = 1;
    acc[1][mod[2][1]].num = 1;

    f.acc = mod[1][1];
    f.ans = 1;
    f.num = 0;

    q.push(f);

    ans[1][mod[1][1]]++;
    acc[1][mod[1][1]].ans = 1;
    acc[1][mod[1][1]].num = 0;

    while(q.empty() == false)
    {
        f = q.front();
        q.pop();

        if(ans[f.ans%2][f.acc] != 1)
        {
            ans[f.ans%2][f.acc]--;
        }
        else
        {

            ans[f.ans%2][f.acc] = 0;

            if(f.acc == n)
            {
                Output(n,f.ans);
                return ;
            }

            if(f.ans < 30 && acc[f.ans%2][f.acc].num == f.num )
            {
                t.ans = f.ans+1;
                t.acc = (f.acc + mod[1][t.ans])%n;
                t.acc = (t.acc == 0 ? n : t.acc);

                t.num = f.num;

                if(acc[t.ans%2][t.acc].ans != t.ans || t.num < acc[t.ans%2][t.acc].num)
                {
                    acc[t.ans%2][t.acc].ans = t.ans;
                    acc[t.ans%2][t.acc].num = t.num;
                    ans[t.ans%2][t.acc]++;
                    q.push(t);
                }

                t.ans = f.ans+1;
                t.acc = (f.acc + mod[2][t.ans])%n;
                t.acc = (t.acc == 0 ? n : t.acc);
                t.num = f.num + (1<<(f.ans));

                if(acc[t.ans%2][t.acc].ans != t.ans || t.num < acc[t.ans%2][t.acc].num)
                {
                    acc[t.ans%2][t.acc].ans = t.ans;
                    acc[t.ans%2][t.acc].num = t.num;
                    ans[t.ans%2][t.acc]++;
                    q.push(t);
                }
            }
        }
    }
    printf("Impossible\n");
}

int main()
{
    int n,i;

    scanf("%d",&n);

    if(n%10 == 5 || n%10 == 0)
    {
        printf("Impossible\n");
        return 0;
    }

    mod[1][1] = 1%n;
    mod[1][1] = (mod[1][1] == 0 ? n : mod[1][1]);

    mod[2][1] = 2%n;
    mod[2][1] = (mod[2][1] == 0 ? n : mod[2][1]);

    for(i = 2; i <= 30; ++i)
    {
        mod[1][i] = (mod[1][i-1]*10)%n;
        mod[1][i] = (mod[1][i] == 0 ? n : mod[1][i]);

        mod[2][i] = (mod[2][i-1]*10)%n;
        mod[2][i] = (mod[2][i] == 0 ? n : mod[2][i]);
    }

    bfs(n);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
用代码解决这个问题The program committee of the school programming contests, which are often held at the Ural State University, is a big, joyful, and united team. In fact, they are so united that the time spent together at the university is not enough for them, so they often visit each other at their homes. In addition, they are quite athletic and like walking. Once the guardian of the traditions of the sports programming at the Ural State University decided that the members of the program committee spent too much time walking from home to home. They could have spent that time inventing and preparing new problems instead. To prove that, he wanted to calculate the average distance that the members of the program committee walked when they visited each other. The guardian took a map of Yekaterinburg, marked the houses of all the members of the program committee there, and wrote down their coordinates. However, there were so many coordinates that he wasn't able to solve that problem and asked for your help. The city of Yekaterinburg is a rectangle with the sides parallel to the coordinate axes. All the streets stretch from east to west or from north to south through the whole city, from one end to the other. The house of each member of the program committee is located strictly at the intersection of two orthogonal streets. It is known that all the members of the program committee walk only along the streets, because it is more pleasant to walk on sidewalks than on small courtyard paths. Of course, when walking from one house to another, they always choose the shortest way. All the members of the program committee visit each other equally often. Input The first line contains the number n of members of the program committee (2 ≤ n ≤ 105). The i-th of the following n lines contains space-separated coordinates xi, yi of the house of the i-th member of the program committee (1 ≤ xi, yi ≤ 106). All coordinates are integers. Output Output the average distance, rounded down to an integer, that a member of the program committee walks from his house to the house of his colleague.
最新发布
05-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值