hdu 2433


好像是Dijkstra算法的变形,最短路生成树。 题意:我老是看不懂题目说什么,N个城镇,M条边,每条边的距离都是1(当然两个城镇之间可能有多条边,也可能没有边),求城镇i到城镇j的最短路之和,即i=1n(j=1ndis[j])\sum_{i=1}^{n}{ (\sum_{j=1}^{n}{ dis[j] } )},如果有两个城镇之间不连通就输出"INF""INF"。要注意的是每次删边只是临时的,不影响下一次计算,比如第一次删第一条边,第二次删第二条边的时候以前删的边都要还原。 思路: 先预处理出每个最短路生成树,标记每颗树上的边,然后枚举输入的每条边,看这条边是否有重边,有的话不改变最短路之和,没有重边就判断是否在某颗最短路生成树上,在的话就再重新构建这棵最短路生成树(只是临时的)。由于,最短路生成树,最多n-1条边,所以每个点,最多做n-1次,所以复杂度(n-1)nm。 code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm=3e3+5,maxn=105;
template <class T>
inline void read(T &res) {
    char c; T flag = 1;
    while ((c = getchar()) < &#39;0&#39; || c > &#39;9&#39;)
        if (c == &#39;-&#39;)
            flag = -1;
    res = c - &#39;0&#39;;
    while ((c = getchar()) >= &#39;0&#39; && c <= &#39;9&#39;)
        res = res * 10 + c - &#39;0&#39;;
    res *= flag;
}
struct node{
	int u,v;
}to[maxm];
int n,m,total,totalA;
int line[maxn][maxn], sum[maxn];
bool used[maxn][maxn][maxn];//uesd[这颗最短路生成树的根i][某条边的端点][某条边的端点]=1表示这条边在最短路生成树上 
queue<int> q;
int  vis[maxn], dis[maxn];
int bfs(int x, bool mark) {
	memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
    q.push(x);
    vis[x] = 1;
    while(!q.empty()) {
    	int v=q.front(); q.pop();
    	for(int i=1;i<=n;++i) if( !vis[i] && (line[v][i] || line[i][v]) ) {
    		vis[i] = 1;
            if(mark == 0){
				used[x][i][v] = 1; used[x][v][i] = 1;
			}//第一次dfs需要标记最短路生成树的边 
			dis[i] += dis[v]+1;
            q.push(i);
		}
	}
	int ans=0;
	for(int i=1;i<=n;++i) {
		if(!vis[i])	return -1;//本来就有不可达的点  无论有没拆路都是INF 直接返回-1 
		ans+=dis[i];
	}
	return ans;
}
int main() {
	while(~scanf("%d %d", &n, &m)) {
		totalA = 0;
        memset(line, 0, sizeof line);
        memset(used, 0, sizeof used);
		for(int i=1;i<=m;++i) {
			int u,v;	read(u),read(v);
			to[i].u=u,to[i].v=v;
			++line[u][v];
			++line[v][u];
		}
		for(int i=1;i<=n;++i) {
			sum[i] = bfs(i, 0);
			if(sum[i] == -1) {
			    totalA = -1; break; 
			} 
            else
                totalA += sum[i];
		}
		if(totalA==-1) {
			for(int i=1;i<=m;++i) puts("INF");
			continue;
		}
		for(int i=1;i<=m;++i) {
			total = totalA ;  // 使用一个临时代替
			--line[to[i].u][to[i].v];
			--line[to[i].v][to[i].u];
			if( line[to[i].u][to[i].v] )
                printf("%d\n", total);
            else {
                bool f = 0;
				for(int j=1; j<=n; ++j)	if(used[j][to[i].u][to[i].v] == 1) {
					total -= sum[j];
					int tmp = bfs(j, 1);  // 不能把bfs()直接赋给sum[x] 因为拆路只是暂时的
					if(tmp == -1) { f=1; break;}
					total += tmp;
				}
				if(f)
                    puts("INF");
                else 
                    printf("%d\n", total);
			}
			++line[to[i].u][to[i].v];
			++line[to[i].v][to[i].u];
		}
	}
	return 0;
}

hdu 2680


题意:n个车站编号1n1\sim n,m条有向边uvvalu、v、val,表示u到v有一条路,需要花val分钟,给你终点s,还有w个起点(不管你从那个起点出发),求到终点的最小花费,怎样都到不了就输出-1。 踩过的坑思路:首先这是一个有向图,不要两个方向的边都存了,起点有很多,但是终点就一个,存反向图,求终点到所有起点最短路的最小值就可以了,用到的数组不要忘记初始化,写了牛客后写航电老是忘记初始化,done[]忘记初始化wa了好久。 code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005,maxm=1e4+7;
template <class T>
inline void read(T &res) {
    char c; T flag = 1;
    while ((c = getchar()) < &#39;0&#39; || c > &#39;9&#39;)
        if (c == &#39;-&#39;)
            flag = -1;
    res = c - &#39;0&#39;;
    while ((c = getchar()) >= &#39;0&#39; && c <= &#39;9&#39;)
        res = res * 10 + c - &#39;0&#39;;
    res *= flag;
}
int head[maxn],Next[maxm<<1],to[maxm<<1],tot,val[maxm<<1];
void add(int x,int y,int c) {
    to[++tot]=y;
    val[tot]=c;
    Next[tot]=head[x];
    head[x]=tot;
}
struct node{
    int v,dis;
    bool operator<(const node&a) const{
        return dis>a.dis;
    }
};
int n,m,dis[maxn];
bool done[maxn];//done[i]=true表示到结点i的最短路径已经找到
void dijkstra(int s) {
    fill(dis, dis + maxn, 0xfffffff);
    fill(done,done+ maxn, 0);
    dis[s]=0;    //起点到自己的距离是0
    priority_queue<node>q;//优先队列,存结点的信息
    q.push(node{s,0});//起点进栈
    while(!q.empty()) {
        node u=q.top();
        q.pop();
        if(done[u.v]) continue;//丢弃已经找到最短路径的结点,即集合A中的结点
        done[u.v]=true;
        for(int i=head[u.v];i;i=Next[i]) {
            int y=to[i];
            if(done[y])    continue;
            if(dis[y]>val[i]+u.dis) {
                dis[y]=val[i]+u.dis;
                q.push(node{y,dis[y]});
            }
        }
    }
}
int main() {
    int n,m,s,w;
    while(~scanf("%d%d%d",&n,&m,&s)) {
    	fill(head,head+maxn,0);	tot=0;
    	while(m--) {
    		int u,v,w;
    		read(u),read(v),read(w);
    		add(v,u,w);
		}
		dijkstra(s);
		int ans=0xfffffff,qi; read(w);
		while(w--) {
			read(qi);
			ans=min(ans,dis[qi]);
		}
		if(ans==0xfffffff)
			puts("-1");
		else
			printf("%d\n",ans);
	}
}

hdu 4889


题意:给出spfa_slf优化的代码,要你写示例使得代码坏掉,即使题中给出的程序按该图执行 最终变量doge大于输入的C跳出 这代码看不下去,不会写。 code:

#include<cstdio>
int main(){
    int i,j;
    int f[33]={-1};
    for(i=1;i<=30;i++) f[i]=f[i-1]*2;
    while(~scanf("%d",&i)){
        puts("99 87");
        for(i=1;i<30;i++){
            printf("%d %d 0\n",i,i+30);
            printf("%d %d %d\n",i,i+1,f[29-i]);
            printf("%d %d %d\n",i+30,i+1,f[30-i]);
        }
    }
    return 0;
}

hdu 4568


最短路算法+旅行商问题(dp状压),注意到k非常小,这样很显然需要状压。 题意:给定一个n*m的棋盘,每一个格子有一个值,代表经过这个格子的花费(-1表示不能走这个格子),给出k个宝藏点的坐标,求从棋盘的任意一个边界进入棋盘,经过所有的宝藏点后在再走出棋盘所需要的最小花费(终点和起点可以不同)。 解题思路:spfa处理处任意两个宝藏点之间的最短距离(最小花费)和每个宝藏点和边界的最短距离。然后状态压缩:dp[s][i]表示经过集合s到达点i的花费(s采用二进制存储的形式,表示集合中的元素,列如s=3,3的二进制是11,表示第0个和第1个宝藏在集合s里)。 状态转移方程见代码,不难理解,别用memset给数组初始化为无穷,虽然可以,但是会超时。 code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
int n,m;
struct node{
    int x,y;
}pos[15];//存宝藏的坐标 
int dx[4][2]= {1,0,0,1,-1,0,0,-1};
int dis[maxn][maxn],a[maxn][maxn],border[25],line[20][20];
bool vis[maxn][maxn];//vis[][]=1表示在队列里 
int dp[1<<15][15];
void spfa(int s) {
	for(int i=0;i<n;i++)
    for(int j=0;j<m;j++)
        dis[i][j]=0x3f3f3f3f;
	memset(vis,0,sizeof vis);
	queue<node>q;
	q.push({pos[s].x,pos[s].y}) ;
	//vis[point[s].x][point[s].y]=1;
	dis[pos[s].x][pos[s].y]=0;
	while(!q.empty()) {
		int x=q.front().x,y=q.front().y;
		q.pop();
		vis[x][y]=0;
		if( !x || x==n-1 || !y || y==m-1 )
            border[s]=min(border[s],dis[x][y]);//求x到边界的最短路
		for(int i=0;i<4;++i) {
			int xx=x+dx[i][0],yy=y+dx[i][1];
			if( xx<0 || xx>=n && yy<0 || yy>=m || a[xx][yy]==-1 ) 
				continue;
			if(dis[xx][yy]>dis[x][y]+a[xx][yy]) {
				dis[xx][yy]=dis[x][y]+a[xx][yy];
				if(!vis[xx][yy]) {
                    vis[xx][yy]=1;
                    q.push({xx,yy});
                }
			}
		} 
	}
}
int main() {
	int T,k;
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
        	scanf("%d",&a[i][j]);
        scanf("%d",&k);
        for(int i=0;i<k;++i)
			scanf("%d%d",&pos[i].x,&pos[i].y);
        for(int i=0;i<k;++i) {
        	border[i]=0x3f3f3f3f;
        	line[i][i]=0;
        	for(int j=i+1;j<k;++j)
				line[i][j]=line[j][i]=0x3f3f3f3f;
		}
		for(int i=0;i<(1<<k);++i)
		for(int j=0;j<k;j++)
			dp[i][j]=0x3f3f3f3f;
		for(int i=0;i<k;++i){
            spfa(i);
            for(int j=0; j<k; ++j) {
                if(j==i)continue;
                line[i][j]=min(dis[pos[j].x][pos[j].y],line[i][j]);
            }
            dp[1<<i][i]=border[i]+a[pos[i].x][pos[i].y];
        }
        for(int s=0;s<(1<<k);++s)
        for(int i=0;i<k;i++) {
            if(s&(1<<i)==0) continue;//s的集合里没有i
            for(int j=0;j<k;j++) {
                if(s&(1<<j))continue;
                dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+line[i][j]);
            }
        }
        int ans=0x3f3f3f3f;
        for(int i=0;i<k;++i)
        	ans=min(ans,dp[(1<<k)-1][i]+border[i]);
        printf("%d\n",ans);
	}
	return 0; 
}