Aizu 1311 Test Case Tweaking DAG上的dp

题目链接:点击打开链接

题意:

给定n个点m条有向边 常数c

下面m行给出边。

修改最少的边的边权使得1->n的最短路长度恰好为c(输入保证1->n存在最短路且最短路权值>c)

思路:

因为是DAG,而且c不大,所以应该是DAG上的dp,反向建边然后bfs出去。

dp[i][j]表示点i距离终点距离恰好为j时最少需要修改的边数。

初始状态为dp[n][0] = 0, 其他为inf。

从u转移到v,若边权不修改则:

1、dp[v][j+edge.dis] = min(dp[u][j]);

若修改这条边权则方程为:

2、dp[v][j] = min(dp[u][ j-? ] +1);

显然2的转移复杂度就是c*c,这样就太大了。


其实若把最短路修改为恰好为c 和 修改为<=c的答案是一样的,因为c越小答案应该越大,所以我们把题意转换成修改为<=c,

则ans = min(dp[n][ 0->c ])

这样2的方程就能改成dp[v][j] = min( dp[u][j]+1 ); 

O(1)的转移。

复杂度为O(m*c)

over.

#include <stdio.h>    
#include <string.h>    
#include <set>  
#include <map>  
#include <algorithm>  
#include <iostream>  
#include <vector>  
#include <string>  
#include <queue>
#include <cmath>  
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;
const int inf = 1e8;
const int N = 105;
const int M = 100010;
struct Edge{
	int from, to, dis, nex;
}edge[1005];
int head[N], edgenum;
void add(int u, int v, int dis){
	Edge E = { u, v, dis, head[u] };
	edge[edgenum] = E;
	head[u] = edgenum++;
}
void init(){ memset(head, -1, sizeof head); edgenum = 0; }
int n, m, c;
int dp[N][M], vis[N];
void bfs(){
	queue<int>q;
	q.push(n);
	dp[n][0] = 0;
	while (!q.empty()){
		int u = q.front(); q.pop(); vis[u] = 0;
		for (int i = head[u]; ~i; i = edge[i].nex){
			int v = edge[i].to;
			bool ok = false;
			for (int j = 0; j <= c; j++)
			{
				if (dp[u][j] == inf)continue;
				if (dp[v][j] > dp[u][j] + 1)
				{
					dp[v][j] = dp[u][j] + 1; ok = true;
				}
				if (j + edge[i].dis <= c && dp[v][j + edge[i].dis] > dp[u][j])
				{
					dp[v][j + edge[i].dis] = dp[u][j]; ok = true;
				}
			}
			if (ok && vis[v] == 0)vis[v] = 1, q.push(v);
		}
	}
}
int main(){
	int u, v, d;
	while (cin>>n>>m>>c, n+m+c){
		init();
		memset(vis, 0, sizeof vis);
		for (int i = 1; i <= n; i++)for (int j = 0; j <= c; j++)dp[i][j] = inf;
		while (m--)
		{
			rd(u); rd(v); rd(d);
			add(v, u, d);
		}
		bfs();
		int ans = inf;
		for (int i = 0; i <= c; i++)ans = min(ans, dp[1][i]);
		pt(ans); puts("");
	}
	return 0;
}
/*
3 3 3
1 2 10
1 2 5
2 3 3

4 5 3
1 2 10
1 2 5
2 3 3
3 4 1
4 2 6

4 5 1
1 2 10
1 2 5
2 3 3
3 4 1
4 2 6


4 5 8
1 2 10
1 2 5
2 3 3
3 4 1
4 2 6

4 5 0
1 2 10
1 2 5
2 3 3
3 4 1
4 2 6


*/  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值