USACO 6.1.1 Postal Vans 特例dp

16 篇文章 0 订阅
1 篇文章 0 订阅

http://train.usaco.org/usacoprob2?a=iHzQON4VeJX&S=vans

题目大意:由4条横线,N条纵线划划分的网络,要求一条路线经过每个网点一次且仅一次,求共有多少条这样的路径


想法一:枚举,不可能,其实到第五章之后的题目好像都不太能枚举了

想法二:相当于先有一个最外层的大矩形轮廓,然后逐步“下凹”,只要下凹的那一格在当前外表面轮廓组成中只有一条边就不会使最后的结果缺失某个点,而且N确定时下凹的次数是固定的,但是要找到下凹的位置麻烦

想法三:与下凹相反,在网络中选择格子形成一个联通的区域,N确定时格子总数也是确定的,但是要求选择区域在任意位置宽度为一且四角的格子要被包含在这个区域中


正解:基于连通性状态压缩的动态规划

说实话以我现在的能力看不太懂,但这个方法绝对是具有普适性的,所以等以后再好好学习

链接:https://wenku.baidu.com/view/a6dce6c76137ee06eff918d1.html 陈丹琦


特解:因为这道题在横向上只有四条线,所以可以用普通的dp方法处理,真的妙

链接:http://blog.csdn.net/jiangshibiao/article/details/21446033

需要注意的一点就是最后的结果很大,当N = 1000时,结果的位数有400+位,需要自定大数类

/*
ID: frontie1
TASK: vans
LANG: C++
*/
#include <iostream>
#include <cstdio>

using namespace std;

struct number
{
    int arr[500];
    int length;

    number()
    {
//        for(int i = 0; i < 100; ++i){
//            arr[i] = 0;
//        }
        length = 1;
    }

    number operator+(const number &obj)
    {
//        this->print();
//        cout << " + ";
//        obj.print();
//        cout << " = ";
        number output;
        int cur1 = 0, cur2 = 0, cur3 = 0;
        int up = 0;
        while(cur1 < length && cur2 < obj.length){
            output.arr[cur3] = arr[cur1] + obj.arr[cur2] + up;
            up = output.arr[cur3] / 10;
            output.arr[cur3] %= 10;
            ++cur1; ++cur2; ++cur3;
        }
        while(cur1 < length){
            output.arr[cur3] = arr[cur1] + up;
            up = output.arr[cur3] / 10;
            output.arr[cur3] %= 10;
            ++cur1; ++cur3;
        }
        while(cur2 < obj.length){
            output.arr[cur3] = obj.arr[cur2] + up;
            up = output.arr[cur3] / 10;
            output.arr[cur3] %= 10;
            ++cur2; ++cur3;
        }
        if(up){
            output.arr[cur3] = up;
            ++cur3;
        }
        output.length = cur3;
//        output.print();
//        cout << endl;
        return output;
    }

    number operator-(const number &obj)
    {
//        this->print();
//        cout << " - ";
//        obj.print();
//        cout << " = ";
        number output;
        int cur1 = 0, cur2 = 0, cur3 = 0;
        int down = 0;

        while(cur1 < length && cur2 < obj.length){
            output.arr[cur3] = arr[cur1] - obj.arr[cur2] - down;
            if(output.arr[cur3] < 0){
                //cout << output.arr[cur3] << " occurred" << endl;
                output.arr[cur3] += 10;
                down = 1;
            }
            else down = 0;
            ++cur1; ++cur2; ++cur3;
        }
        while(cur1 < length){
            output.arr[cur3] = arr[cur1] - down;
            if(output.arr[cur3] < 0){
                output.arr[cur3] += 10;
                down = 1;
            }
            else down = 0;
            ++cur1; ++cur3;
        }
        while(output.arr[cur3-1] == 0) --cur3;
        output.length = cur3;
//        output.print();
//        cout << endl;
        return output;
    }

    void print() const
    {
        for(int i = length-1; i >= 0; --i){
            cout << arr[i];
        }
    }
};

int N;

number f[1010];
number g[1010];

//int f[1010];
//int g[1010];


int main()
{
    //freopen("vans.in", "r", stdin);
    //freopen("vans.out", "w", stdout);

    cin >> N;

//    f[1] = 0; f[2] = 2;
//    g[1] = 2; g[2] = 2;
//
//    for(int i = 3; i <= N; ++i){
//        f[i] = g[i-1] + f[i-1];
//        g[i] = 2*f[i-1] + g[i-1] + g[i-2] - g[i-3];
//        cout << f[i] << '\t' << g[i] << endl;
//    }

    f[1].arr[0] = 0; f[2].arr[0] = 2; f[3].arr[0] = 4;
    g[1].arr[0] = 2; g[2].arr[0] = 2; g[3].arr[0] = 8;


    for(int i = 4; i <= N; ++i){
        f[i] = g[i-1] + f[i-1];
        g[i] = ((f[i-1] + f[i-1]) + (g[i-1] + g[i-2])) - g[i-3];
//        f[i].print();
//        cout << '\t';
//        g[i].print();
//        cout << endl;
    }

    f[N].print();
    cout << endl;
//    cout << f[N].length << endl;

//    for(int i = f[N].length-1; i >= 0; --i){
//        cout << f[N].arr[i];
//    }
//    cout << endl;


    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值