您的位置:首页 > 其它

HDU 3001(状态压缩dp)

2016-08-12 16:05 423 查看
`这是我第一次写的过了所有的样例,但是WA因为题目是说每个点可以走两次,而我刚开始以为这题就是裸的旅行家问题,后台数据一定专门设置了某个点重复走两次会更小的情况,所以这里需要一种新的方法,三进制!标记

提供几组测试数据:

输入

5 5

2 1 1

3 1 1

4 1 1

5 1 1

2 5 10

答案

14

输入

8 9

2 1 1

3 1 1

4 1 1

5 1 1

2 5 10

4 7 1

4 6 1

4 8 1

8 7 10

答案

27

输入

1 0

答案

0

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include<algorithm>
using namespace std;

const int INF = 0x3f3f3f3f;
int route[15][15];
int dp[1 << 10][10];

int main()
{
int n, m;
while (~scanf("%d%d", &n, &m))
{
memset(route, INF, sizeof(route));
memset(dp, INF, sizeof(dp));
for (int i = 0; i < m; i++)
{
int a, b;
cin >> a >> b;
cin >> route[a-1][b-1];
route[b-1][a-1] = route[a-1][b-1];
}
for (int i = 0; i < n; i++)
{
route[i][i] = 0;
}
int end =( 1 << n ) - 1;
for (int i = 0; i < n; i++)
{
dp[0][i] = 0;
}
for (int i = 0; i <= end; i++)
{
for (int j = 0; j < n; j++)
{
for (int k = 0; k < n; k++)
{
int temp = 1 << k;
if ((temp&i)==0)
{
int curtemp = i |temp;
if (dp[i][j] + route[j][k] < dp[curtemp][k])
{
dp[curtemp][k] = dp[i][j] + route[j][k];
}
}
}
}
}
int Min=INF;
for (int i = 0; i < n; i++)
{
Min = min(Min, dp[end][i]);
}
if (Min > INF - 100)
{
cout << -1 << endl;
}
else
{
cout << Min << endl;
}
}
return 0;
}


正确AC代码

#pragma warning(disable:4996)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include<algorithm>
using namespace std;

const int INF = 0x3f3f3f3f;
int State[12];//存储3进制
int visited[60000][12];
int dp[60000][12];//状态为i时以j结尾的最小权值
int map[12][12];//存储图
int n, m;

void Initiate()
{
State[0] = 1;
for (int i = 1; i <= 10; i++)
State[i] = State[i - 1] * 3;//  State {1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049, 0}存储3进制每一位对应的数值

for (int i = 0; i <= State[10]; i++)//将 所有不超过59049的数以三进制的形式存储下来,
//这样只需要处理一边节省时间,如果是只能访问一边不需要处理因为,电脑本身就是二进制
{
int temp = i;
for (int j = 0; j <= 10; j++)
{
visited[i][j] = temp % 3;//每个位置对应的处理号,每个位置i状态对应的访问状况
temp /= 3;
}
}
}

int main()
{
Initiate();
int u, v, w;
while (~scanf("%d%d", &n, &m))
{
memset(dp, INF, sizeof(dp));
memset(map, INF, sizeof(map));
for (int i = 0; i<n; i++)
dp[State[i]][i] = 0;
while (m--)//这题比较坑的地方是它输入的时候,两点之间就会输多条边,我们只需要存储最小的就可以了
{
scanf("%d%d%d", &u, &v, &w);
u--;
v--;
map[u][v] = map[v][u] = min(map[u][v], w);//无向图两边都要存储
}
int Max = INF;
for (int i = 0; i<State
; i++)//枚举所有状态
{
bool flag = true;
for (int j = 0; j<n; j++)//枚举终点
{
if (visited[i][j] == 0)
flag = false;//是否每个城市都至少走了1次
if (dp[i][j] == INF)//该状态不合法,比如dp[101][1]表示已经访问了2号城市和0号城市
continue;           //以1号城市结尾,这样就是不合法的
for (int k = 0; k<n; k++)//枚举没有包括的点
if (j != k)
{
if (visited[i][k] >= 2)//已经访问了两次
continue;
if (map[j][k] == INF)//两点之间不存在回路
continue;
dp[i + State[k]][k] = min(dp[i + State[k]][k], dp[i][j] + map[j][k]);
//state[k]是已经处理好的类似于二进制中的i|1<<k
}
}
if (flag)
{
for (int j = 0; j<n; j++)
{
Max = min(Max, dp[i][j]);
}
}
}
if (Max == INF)//无法遍历所有点
Max = -1;
cout << Max << endl;
}
return 0;
}


#pragma warning(disable:4996)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include<algorithm>
using namespace std;

const int INF = 0x3f3f3f3f;
int state[12];
int visited[80000][15];
int map[15][15];
int dp[80000][15];

void Init()
{
state[0] = 1;
for (int i = 1; i <= 10; i++)
{
state[i] = state[i - 1] * 3;
}
for (int i = 0; i < state[10] - 1; i++)
{
int temp = i;
for (int j = 0; j < 10; j++)
{
visited[i][j] = temp % 3;
temp /= 3;
}
}
}

int main()
{
//freopen("in.txt", "r", stdin);
Init();
int n, m;
while (cin >> n >> m)
{
memset(dp, INF, sizeof(dp));
memset(map, INF, sizeof(map));
for (int i = 0; i<m; i++)
{
int start, end;
int temp;
cin >> start >> end;
start--;
end--;
cin >> temp;
map[start][end] = map[end][start] = min(map[start][end], temp);
}
for (int i = 0; i < n; i++)
{
map[i][i] = 0;
}
for (int i = 0; i <n; i++)
{
dp[0][i] = 0;
}
for (int i = 0; i <= state
- 1; i++)
{
for (int j = 0; j < n; j++)
{
for (int k = 0; k < n; k++)
if (visited[i][k]<2)
{
if (dp[i][j] + map[j][k]<= dp[i + state[k]][k])
dp[i + state[k]][k] = dp[i][j] + map[j][k];
}
}
}
int Min = INF;
int S = 0;
for (int i = 0; i <n; i++)
{
S += state[i];
}
for (int i = S; i <= state
- 1; i++)
{
int flag = 1;
for (int j = 0; j < n; j++)
{
if (visited[i][j] == 0)
flag = 0;
}
if (flag)
for (int j = 0; j < n; j++)
{
Min = min(dp[i][j], Min);
}
}
if (Min == INF)
cout << "-1" << endl;
else
cout << Min << endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp 压缩 HDU