标签:大数 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]。(下面的代码某个处理似乎没有必要)
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],然后加上路程乘以未被关掉的所有灯的功率。
至于这个功率可以用前缀和先预处理出,接着用总功率减去这一段的功率就行了。
由于开始以为灯的位置不是有序的,特意排了道序,可以无视。
#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; }
(番外话)
标签:大数 fstream sizeof stat 角度 fst namespace div lin
原文地址:http://www.cnblogs.com/yyf0309/p/6441976.html