码迷,mamicode.com
首页 > 其他好文 > 详细

2016.2.24 dp练习

时间:2017-02-25 17:12:19      阅读:249      评论:0      收藏:0      [点我收藏+]

标签:大数   fstream   sizeof   stat   角度   fst   namespace   div   lin   

技术分享


  很经典的一道状压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;
}

(番外话)

2016.2.24 dp练习

标签:大数   fstream   sizeof   stat   角度   fst   namespace   div   lin   

原文地址:http://www.cnblogs.com/yyf0309/p/6441976.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!