1. 程式人生 > >bzoj4500 矩陣 差分約束系統

bzoj4500 矩陣 差分約束系統

Description


有一個n*m的矩陣,初始每個格子的權值都為0,可以對矩陣執行兩種操作:
1.選擇一行,該行每個格子的權值加1或減1。
2.選擇一列,該列每個格子的權值加1或減1。
現在有K個限制,每個限制為一個三元組(x,y,c),代表格子(x,y)權值等於c。
問是否存在一個操作序列,使得操作完後的矩陣滿足所有的限制。
如果存在輸出”Yes”,否則輸出”No”。

先輸入一個T(T <= 5)代表輸入有T組資料,每組資料格式為:
第一行三個整數n, m, k (1 <= n, m,k <= 1000)。
接下來k行,每行三個整數x, y, c。

Solution


我是誰,差分約束是啥,這是哪

考慮記行操作為x[],列操作為y[],那麼一個限制等價於x[i]+y[j]=c
轉換一下就是x[i]-(-y[j])<=c,-y[j]-x[i]<=-c
注意到這個和最短路的鬆弛條件很像,即最短路上滿足dis[y]-dis[x]<=w[x][y]
那麼對於這種形式的限制連邊y[j]到x[i]跑最短路就行了。不需要輸出方案就判環嘛

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include
<queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i) #define fill(x,t) memset(x,t,sizeof(x)) const int N=4005; const int E=10005; struct edge {int y,w,next;} e[E]; bool vis[N]; int dis[N],cnt[N]; int ls[N],edCnt; int read() { int x=0,v=1; char ch=getchar(); for (;ch<'0'||ch>'9';v=(
ch=='-')?(-1):(v),ch=getchar()); for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar()); return x*v; } void add_edge(int x,int y,int w) { e[++edCnt]=(edge) {y,w,ls[x]}; ls[x]=edCnt; } bool spfa(int st) { std:: queue <int> que; vis[st]=true; dis[st]=0; bool ret=false; for (que.push(st);!que.empty();) { int now=que.front(); que.pop(); if (++cnt[now]==1000) return true; for (int i=ls[now];i;i=e[i].next) { if (dis[now]+e[i].w<dis[e[i].y]) { dis[e[i].y]=dis[now]+e[i].w; if (!vis[e[i].y]) { que.push(e[i].y); vis[e[i].y]=true; } } } vis[now]=false; } return false; } int main(void) { for (int T=read();T--;) { edCnt=0; fill(ls,0); fill(vis,0); int n=read(),m=read(),k=read(); rep(i,1,k) { int x=read(),y=read(),c=read(); add_edge(y+n,x,c); add_edge(x,y+n,-c); } fill(dis,63); fill(cnt,0); bool flag=false; rep(i,1,n+m) if (dis[i]==dis[0]) { flag|=spfa(i); } flag?puts("No"):puts("Yes"); } return 0; }