首页 > 代码库 > 2016.2.24 dp练习

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 0xfffffff17 #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练习