首页 > 代码库 > 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练习
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。