最近遇到的这种题有点多。。。干脆做一些题集吧。
(不定期更新)
题意: 给你 n 个点 , m 条边 , k条免费路径的权限
然后让你求最短路
直接套SPFA ,然后加点修改
定义: 我们定义一个二维数组ddt[i][j] , 表示第 i 个点 , 免费了 j 条路 ;
故而, 我们的标记数组也不必多说 findv[i][j] 。
然后存好图就直接跑就行了。
算法思想:
对于我们当前找到的终点,尝试起点的状态去更新,不选择此条边免费的状态和选择此条边免费的状态,再将这两个状态压入队列去更新可以到达的其他状态。
代码的题目为2018年南京网络赛里面的一道图论题
代码一: (这个是一个学长写的 = =)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define PI acos(-2)
#define llf float
#define inf 0x7f7f7f7f
#define INF 0x3f3f3f3f3f3f3f3f
const int maxn = 1e5 + 10;
typedef long double lllld;
typedef long long ll;
typedef pair<int, int> ii;
typedef pair<double, double> ldd;
typedef string ss;
typedef vector vss;
int findv[11][maxn];
ll ddt[11][maxn];
int n, idx, k;
#define read(FILENAME) freopen((FILENAME + ".in").c_str(), "r", stdin)
#define write(FILENAME) freopen((FILENAME + ".out").c_str(), "w", stdout)
struct point {
int to, next;
ll val;
}E[maxn * 4];
int head_1[maxn];
struct Stu {
int p;
int u;
ll d;
};
void init() {
idx = 0;
memset(head_1, -1, sizeof(head_1));
}
void solve();
void add(int x, int y, ll val);
int main() {
int c, b, a;
int i;
int tt;
scanf("%d", &tt);
int x;
while (tt--) {
int m;
scanf("%d%d%d", &n, &m, &k);
init();
for (i = 0; i < m; i++) {
scanf("%d%d%d", &a, &b, &c);
add(a, b, (ll)c);
}
solve();
}
return 0;
}
void solve() {
queue QQ;
memset(findv, 0, sizeof(findv));
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= k; j++)
ddt[j][i] = INF;
}
for (int i = 0; i <= k; i++) ddt[i][1] = 0;
QQ.push(Stu{0, 1, 0});
findv[0][1] = 1;
int i;
int ss = 0;
int sss = 100005;
while (!QQ.empty()) {
ss++;
if (ss > sss) break;
Stu now = QQ.front();
QQ.pop();
findv[now.p][now.u] = 0;
for (i = head_1[now.u]; i != -1; i = E[i].next) {
int to = E[i].to;
if (ddt[now.p][to] > ddt[now.p][now.u] + E[i].val) {
ddt[now.p][to] = ddt[now.p][now.u] + E[i].val;
if (findv[now.p][to] == 0) {
QQ.push(Stu{now.p, to, ddt[now.p][to]});
findv[now.p][to] = 1;
}
}
if (ddt[now.p + 1][to] > ddt[now.p][now.u]) {
ddt[now.p + 1][to] = ddt[now.p][now.u];
if (findv[now.p + 1][to] == 0 && now.p + 1 <= k) {
QQ.push(Stu{now.p + 1, to, ddt[now.p][to]});
findv[now.p + 1][to] = 1;
}
}
}
}
long long flag = INF;
for (i = 0; i <= k; i++) {
flag = min(flag, ddt[i][n]);
}
printf("%lld\n", flag);
}
void add(int x, int y, ll val) {
E[idx].to = y;
E[idx].val = val;
E[idx].next = head_1[x];
head_1[x] = idx++;
}
代码二: 一样的方法,不过第一个是spfa , 第二个是dijstra
(这个是我看着人家的题解一个一个慢慢敲出来的 = =)
#include
using namespace std ;
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
const int maxn = 1e6 + 10 ;
int vis[maxn][20] ;
int n , m , k ;
LL dis[maxn][20] ;
int head[maxn] ;
struct node{
int to , next ;
LL val ;
node(){} ;
node(int to, int nx, LL w) : to(to), next(nx), val(w) {}
}st[maxn ] ;
struct NODE{
int xp , to ;
LL w ;
NODE(){} ;
NODE(int t , int p , LL val){ to = t ; xp = p ; w = val ; }
bool operator < (const NODE &r) const
{
return w > r.w;
}
};
int cnt ;
void add(int u, int v, LL w)
{
st[++cnt] = node(v, head[u], w); head[u] = cnt;
}
void dijstra(){
for(int i = 1 ; i <= n ; i++) for(int j = 0 ; j <= k ; j++) dis[i][j] = INF , vis[i][j] = 0 ;
priority_queue q ; q.push(NODE(1 ,0 , 0 )) ; dis[1][0] = 0 ;
while(!q.empty()){
int u = q.top().to ;
int p = q.top().xp ;
LL cost = q.top().w ;
q.pop() ;
// cout <
if(vis[u][p] == 1) continue ;
vis[u][p] = 1 ;
dis[u][p] = cost ;
for(int i = head[u] ; i != -1 ; i = st[i].next ){
int v = st[i].to ;
int c = st[i].val;
if(dis[u][p] + c < dis[v][p] ) { dis[v][p] = dis[u][p] + c ; q.push(NODE(v, p ,dis[v][p] )) ; }
if(p + 1 <= k && dis[u][p]< dis[v][p+1] ) { dis[v][p+1] = dis[u][p] ; q.push(NODE(v , p+1 , dis[v][p+1] ) ) ; }
}
}
}
void init(){
memset(head , -1 , sizeof(head)) ;
cnt = 0 ;
}
int main(){
int t ;
cin >> t ;
while(t--){
init() ;
scanf("%d %d %d",&n,&m,&k) ;
int u , v ;
LL w ;
for(int i = 0 ; i < m ; i++){
scanf("%d%d%lld",&u,&v,&w) ;
add(u , v , w ) ;
}
dijstra();
LL ans = dis[n][k] ;
printf("%lld\n",ans) ;
}
return 0 ;
}
题二 :
CCF无线网络
题目:
问题描述
目前在一个很大的平面房间里有 n 个无线路由器,每个无线路由器都固定在某个点上。任何两个无线路由器只要距离不超过 r 就能互相建立网络连接。
除此以外,另有 m 个可以摆放无线路由器的位置。你可以在这些位置中选择至多 k 个增设新的路由器。
你的目标是使得第 1 个路由器和第 2 个路由器之间的网络连接经过尽量少的中转路由器。请问在最优方案下中转路由器的最少个数是多少?
输入格式
第一行包含四个正整数 n,m,k,r。(2 ≤ n ≤ 100,1 ≤ k ≤ m ≤ 100, 1 ≤ r ≤ 108)。
接下来 n 行,每行包含两个整数 xi 和 yi,表示一个已经放置好的无线 路由器在 (xi, yi) 点处。输入数据保证第 1 和第 2 个路由器在仅有这 n 个路由器的情况下已经可以互相连接(经过一系列的中转路由器)。
接下来 m 行,每行包含两个整数 xi 和 yi,表示 (xi, yi) 点处可以增设 一个路由器。
输入中所有的坐标的绝对值不超过 108,保证输入中的坐标各不相同。
输出格式
输出只有一个数,即在指定的位置中增设 k 个路由器后,从第 1 个路 由器到第 2 个路由器最少经过的中转路由器的个数。
样例输入
5 3 1 3
0 0
5 5
0 3
0 5
3 5
3 3
4 4
3 0
样例输出
2
思路:
在这个题中,我们设立dis[n+m][k] 的二维数组表示 第N个点,使用了k个可选取的点, 然后跑SPFA,在其中加入队列的操作中添加一些条件就好,求出第一个点到第二个点的距离。
然后我们遍历下dis[1][i] 这个数组,然后取最小值即可 。
参考链接(CCF无线网络)
代码如下:
#include
#include
#include
#include
#include
#include
using namespace std ;
int head[10050] ;
int tot = 0 ;
int dis[10500][210] ;
int vis[10500][210] ;
int n , m , k , r ;
struct Edge
{
int to,next;
}edge[10050];
struct node{
int u , v ;
};
void addedges(int u,int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
edge[tot].to = u;
edge[tot].next = head[v];
head[v] = tot++;
}
void init(){
tot = 0 ;
memset(head , -1 , sizeof(head)) ;
}
int x[1050] , y[1050] ;
void SPFA(){
memset(vis , 0 , sizeof(vis ) ) ;
memset(dis , 0x3f , sizeof(dis ) ) ;
queue q ;
node a , now ;
a.u = 0 , a.v = 0 ;
q.push(a) ; dis[0][0] = 0 ; vis[0][0] = 1 ;
while(!q.empty()){
now = q.front() ; q.pop() ;
int u = now.u ;
int kk = now.v ;
vis[u][kk] = 0 ;
for(int i = head[u] ; i!= -1 ; i = edge[i].next ){
int v = edge[i].to ;
int kk = now.v ;
if(v >= n) kk++ ;
if(dis[u][now.v] + 1 < dis[v][kk] && kk <= k ) {
dis[v][kk] = dis[u][now.v] + 1 ;
a.v = kk ; a.u = v ;
if(vis[v][kk] == 0 ) {
vis[v][kk] = 1 ;
q.push(a) ;
}
}
}
}
}
int p[1050][1050] ;
int main(){
cin >> n >> m >> k >>r ;
init();
for(int i=0;iscanf("%d%d",&p[i][0],&p[i][1]);
for(int i=0;ifor(int j=i+1;jint x = p[i][0] - p[j][0];
int y = p[i][1] - p[j][1];
if( (long long int)x*x +(long long int) y*y <=(long long int) r*r ) addedges(i,j);
}
}
SPFA( ) ;
int ans = 999 ;
/* for(int i = 0 ; i <= n+m ; i++){
for(int j = 0 ; j <= n+m ; j++)
cout << dis[i][j] <<" " ;
cout <
for(int i=0;i<=k;i++) ans = min(ans ,dis[1][i]) ;
printf("%d\n",ans - 1);
return 0;
}