2016.2.24 dp练习



  很经典的一道状压dp(似乎叫做旅行商问题),用f[i][s]表示在到达点i,已经经过的城市用二进制表示为s,于是方程就很简单了:

f[i][s] = min { f[j][s ^ (1 << j)] + dis[j][i]| s & (1 << j) != 0}

  然后用记忆化搜索即可,注意方向,因为dis[i][j]可能不等于dis[j][i]。(下面的代码某个处理似乎没有必要)

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<fstream>
 7 #include<sstream>
 8 #include<algorithm>
 9 #include<map>
10 #include<set>
11 #include<queue>
12 #include<vector>
13 #include<stack>
14 using namespace std;
15 typedef bool boolean;
16 #define INF 0xfffffff
17 #define smin(a, b) a = min(a, b)
18 #define smax(a, b) a = max(a, b)
19 template<typename T>
20 inline void readInteger(T& u){
21     char x;
22     int aFlag = 1;
23     while(!isdigit((x = getchar())) && x != ‘-‘);
24     if(x == ‘-‘){
25         x = getchar();
26         aFlag = -1;
27     }
28     for(u = x - ‘0‘; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - ‘0‘);
29     ungetc(x, stdin);
30     u *= aFlag;
31 }
32
33 int n;
34 int dis[16][16];
35 int f[16][(1 << 17)];
36 boolean vis[16][(1 << 17)];
37
38 inline void init() {
39     readInteger(n);
40     for(int i = 1; i <= n; i++)
41         for(int j = 1; j <= n; j++)
42             readInteger(dis[i][j]);
43     for(int i = 1; i <= n; i++)
44         dis[i][0] = dis[i][1], dis[0][i] = dis[1][i];
45 }
46
47 int dfs(int local, int status) {
48     if(vis[local][status])    return f[local][status];
49     vis[local][status] = true;
50     for(int i = 0; i <= n; i++) {
51         if(status & (1 << i)) {
52             int ret = dfs(i, status ^ (1 << i));
53             smin(f[local][status], f[i][status ^ (1 << i)] + dis[i][local]);
54         }
55     }
56     return f[local][status];
57 }
58
59 inline void solve() {
60     memset(vis, false, sizeof(vis));
61     memset(f, 0x7f, sizeof(f));
62     vis[1][0] = true;
63     f[1][0] = 0;
64     int res = dfs(0, (1 << (n + 1)) - 2);
65     printf("%d", res);
66 }
67
68 int main() {
69     freopen("salesman.in", "r", stdin);
70     freopen("salesman.out", "w", stdout);
71     init();
72     solve();
73     return 0;
74 }





  因为关灯不耗时间,所以从一个地方走到另一个地方,从贪心的角度来讲,肯定要把沿路的灯都关掉,因此得到原问题转化成了区间dp。当然还要考虑是从做走到右还是从右走到左,当然可以直接用f[i][j]表示,但是为了防止各种手抽手贱导致半天调不出来,还是加了一维[0/1],表示在从右走到左(0)还是从左走到右。于是方程很容易就出来了,详细的看代码,这里就简单地写了,从f[i][j]转移到f[i - 1][j]或者f[i][j + 1],然后加上路程乘以未被关掉的所有灯的功率。

  至于这个功率可以用前缀和先预处理出,接着用总功率减去这一段的功率就行了。

  由于开始以为灯的位置不是有序的,特意排了道序,可以无视。

Code

#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<fstream>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
typedef bool boolean;
#ifdef    WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0xfffffff
#define smin(a, b) a = min(a, b)
#define smax(a, b) a = max(a, b)
template<typename T>
inline void readInteger(T& u){
    char x;
    int aFlag = 1;
    while(!isdigit((x = getchar())) && x != ‘-‘);
    if(x == ‘-‘){
        x = getchar();
        aFlag = -1;
    }
    for(u = x - ‘0‘; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - ‘0‘);
    ungetc(x, stdin);
    u *= aFlag;
}

typedef class tower {
    public:
        int pos;
        int w;
        int index;
        tower(const int pos = 0, const int w = 0, const int index = 0):pos(pos), w(w), index(index) {        }

        boolean operator < (tower a) const {
            return pos < a.pos;
        }
}tower;

int n, c, rc;
long long f[2][1005][1005];
tower tows[1005];
long long sumw[1005];

inline void init() {
    readInteger(n);
    readInteger(c);
    for(int i = 1; i <= n; i++) {
        readInteger(tows[i].pos);
        readInteger(tows[i].w);
        tows[i].index = i;
    }
    sort(tows + 1, tows + n + 1);
    sumw[0] = 0;
    for(int i = 1; i <= n; i++) {
        sumw[i] = sumw[i - 1] + tows[i].w;
        if(tows[i].index == c)    rc = i;
    }
}

inline void solve() {
    memset(f, 0x37, sizeof(f));
    f[0][rc][rc] = f[1][rc][rc] = 0;
    if(rc > 1)
        f[0][rc - 1][rc] = (tows[rc].pos - tows[rc - 1].pos) * (sumw[n] - tows[rc].w);
    if(rc < n)
        f[1][rc][rc + 1] = (tows[rc + 1].pos - tows[rc].pos) * (sumw[n] - tows[rc].w);
    for(int l = 1; l < n; l++) {
        for(int i = 1; i + l <= n; i++) {
            int j = i + l;
            if(i > 1) {
                smin(f[0][i - 1][j], f[0][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[i].pos - tows[i - 1].pos));
                smin(f[0][i - 1][j], f[1][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[j].pos - tows[i - 1].pos));
            }
            if(j < n) {
                smin(f[1][i][j + 1], f[1][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[j + 1].pos - tows[j].pos));
                smin(f[1][i][j + 1], f[0][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[j + 1].pos - tows[i].pos));
            }
        }
    }
    long long res = smin(f[0][1][n], f[1][1][n]);
    printf(AUTO, res);
}

int main() {
    freopen("power.in", "r", stdin);
    freopen("power.out", "w", stdout);
    init();
    solve();
    return 0;
}

(番外话)

时间: 2024-08-25 00:10:22

2016.2.24 dp练习的相关文章

BZOJ 4521 CQOI 2016 手机号码 数位DP

4521: [Cqoi2016]手机号码 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 539  Solved: 325[Submit][Status][Discuss] Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不 吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号 码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码

【2016.11.24】HTML笔记与练习

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="Generator" content="EditPlus?"> <meta name="Author" content=""> <meta name="

2016/10/24 笔记总结

1.css3 动画:通过设置图片的opacity 来到hover时的动画: .nr_mod_service_hp_bottom_ts .inner .item .pic a .d_img { filter: alpha(opacity=100); opacity: 1; transition: 0.3s; -webkit-transition: 0.3s; -moz-transition: 0.3s; -ms-transition: 0.3s; -o-transition: 0.3s; } .n

2016.10.24 继续学习

今天继续学习,进入面向对象的学习. 1.输出换行可以这样输出 System.out.println(); 2.数组学得有点蒙.... public class PracMultiArray { public static void main(String[] args) { int[][] a1 = {{1, 2, 4}, {3, 1, 2}, {14, 5, 3}}; System.out.println(a1[1][2]); for(int i=0;i<a1.length;i++){ for

2016.07.24

马上就到七月份的最后一个星期了,自从毕业以后,感觉日子过的好快啊. 距离过年也只有6个月的时间了,其实也就五个月的时间了.五个月的时间,要完成自我的突破 突破方向: 1.linux:最起码的操作应该会吧,然后能在linux安装相关环境,和熟练开发.毕竟许多开源的项目都首先会发布在linux上. 2.java:java的基础知识要都过一遍,现在搞线程,然后io,然后网络. 3.javascript:javascript要好好掌握,说不定以后java不行了,就要靠javascript吃饭了.当然no

2016第24周一

早起被闹钟叫醒去上班,踩着点赶到公司上班,然后浏览网页任务考虑方案对策,和各种同事沟通聊天后到中午吃饭,吃完饭午休,然后到下午又是沟通聊天后开会到晚上,稍微休息一下后又是开会,然后是加班总结工作后到家,失败的一天就像这样,很少有时间精力是个人积极主动的去用时间精力来完成自己的计划目标,总是被周遭的环境被动的推着前进.不要总是低估别人的能力高估自己的能力,也不要总是低估自己的缺点和错误而高估别人的不足. 明天又是一年高考季,祝那些参加高考的同学朋友都考出自己的水平.

2016/2/24 1,css有几种引入方式 2,div除了可以声明id来控制,还可以声明什么控制? 3,如何让2个div,并排显示。

1,css有几种引入方式 使用HTML标签的STYLE属性 将STYLE属性直接加在单个的HTML元素标签上,控制HTML标签的表现样式.这种引入CSS的方式是分散灵活方便,但缺乏整体性和规划性,不利于后期的修改和维护,当需要修改网站的样式时,一个相同的修改可能涉及多个地方,维护成本高.使用STYLE属性的样式效果最强,会覆盖掉其它几种引入方式的相同样式效果.   将样式代码写在页面<STYLE>...</STYLE>标签之中 <STYLE>...</STYLE&

2016.2.24(类)

访问修饰符private,默认,protected,public 在应用时的区别? private:只有本类可以访问 默认:本类,同包可以访问 protected:本类,同包,子类,可以访问 public:可以任意对象访问. 封装有什么好处? 类的成员变量可以成为只读或者只写的 类可对存储在其成员变量中的内容有一个整体的规划 类的用户不需要知道类是如何存储数据的 静态 用static修饰的属性,所有对象共用,实际与对象无关注意:1.与对象无关,与类有关 2.全类共享一个 3.预加载 static

2016/02/24 codes

CrossVF:function (s){var tX = this.x;x.this = s * this.y;this.y = -s * tX;}, CrossFV:function (s){var tX = this.x;this.x = -s * this.y;this.y = s * tX;}, minV:function(b){this.x = this.x < b.x ? this.x: b.x;this.y = this.y < b.y ? this.y: b.y;}, max