2016.2.24 dp练习

Posted 阿波罗2003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

  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 #ifdef    WIN32
 17 #define AUTO "%I64d"
 18 #else
 19 #define AUTO "%lld"
 20 #endif
 21 #define INF 0xfffffff
 22 #define smin(a, b) a = min(a, b)
 23 #define smax(a, b) a = max(a, b)
 24 template<typename T>
 25 inline void readInteger(T& u){
 26     char x;
 27     int aFlag = 1;
 28     while(!isdigit((x = getchar())) && x != \'-\');
 29     if(x == \'-\'){
 30         x = getchar();
 31         aFlag = -1;
 32     }
 33     for(u = x - \'0\'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - \'0\');
 34     ungetc(x, stdin);
 35     u *= aFlag;
 36 }
 37 
 38 typedef class tower {
 39     public:
 40         int pos;
 41         int w;
 42         int index;
 43         tower(const int pos = 0, const int w = 0, const int index = 0):pos(pos), w(w), index(index) {        }
 44 
 45         boolean operator < (tower a) const {
 46             return pos < a.pos;
 47         }
 48 }tower;
 49 
 50 int n, c, rc;
 51 long long f[2][1005][1005];
 52 tower tows[1005];
 53 long long sumw[1005];
 54 
 55 inline void init() {
 56     readInteger(n);
 57     readInteger(c);
 58     for(int i = 1; i <= n; i++) {
 59         readInteger(tows[i].pos);
 60         readInteger(tows[i].w);
 61         tows[i].index = i;
 62     }
 63     sort(tows + 1, tows + n + 1);
 64     sumw[0] = 0;
 65     for(int i = 1; i <= n; i++) {
 66         sumw[i] = sumw[i - 1] + tows[i].w;
 67         if(tows[i].index == c)    rc = i;
 68     }
 69 }
 70 
 71 inline void solve() {
 72     memset(f, 0x37, sizeof(f));
 73     f[0][rc][rc] = f[1][rc][rc] = 0;
 74     if(rc > 1)
 75         f[0][rc - 1][rc] = (tows[rc].pos - tows[rc - 1].pos) * (sumw[n] - tows[rc].w);
 76     if(rc < n)
 77         f[1][rc][rc + 1] = (tows[rc + 1].pos - tows[rc].pos) * (sumw[n] - tows[rc].w);
 78     for(int l = 1; l < n; l++) {
 79         for(int i = 1; i + l <= n; i++) {
 80             int j = i + l;
 81             if(i > 1) {
 82                 smin(f[0][i - 1][j], f[0][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[i].pos - tows[i - 1].pos));
 83                 smin(f[0][i - 1][j], f[1][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[j].pos - tows[i - 1].pos));
 84             }
 85             if(j < n) {
 86                 smin(f[1][i][j + 1], f[1][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[j + 1].pos - tows[j].pos));
 87                 smin(f[1][i][j + 1], f[0][i][j] + (sumw[n] - (sumw[j] - sumw[i - 1])) * (tows[j + 1].pos - tows[i].pos));
 88             }
 89         }
 90     }
 91     long long res = smin(f[0][1][n], f[1][1][n]);
 92     printf(AUTO, res);
 93 }
 94 
 95 int main() {
 96     freopen("power.in", "r", stdin);
 97     freopen("power.out", "w", stdout);
 98     init();
 99     solve();
100     return 0;
101 }

(题外话)

以上是关于2016.2.24 dp练习的主要内容,如果未能解决你的问题,请参考以下文章

2016.2.24. 《构建之法》开始阅读

数位DP练习

算法练习--- DP 求解最长上升子序列(LIS)

spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段

添加两个窗格的平板电脑布局会导致在移动设备中找不到视图(小于w600dp)

Python练习册 第 0013 题: 用 Python 写一个爬图片的程序,爬 这个链接里的日本妹子图片 :-),(http://tieba.baidu.com/p/2166231880)(代码片段