dp练习 2016.2.24
很经典的一道状压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 }
(题外话)