奇技淫巧训练之四
https://www.luogu.org/problem/P2294
本题有多种解法;
贪心
先按左端点为第一排序关键字,再排右端点。
之后就开始两两比较,如果左端点相等,就比较右端点,
如果相等,就比较值,如果值不同,就直接输出false,否则输出true,
如果右端点不等,就把相同的部分抵消掉,把新的区间再压入优先队列。
直到不能操作,就输出true。
很神奇这方法过了
code by std:
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1100
using namespace std;
inline void read(int &x)
{
x=0;
int p=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+(c^'0');c=getchar();}
x*=p;
}
int n,m;
struct node
{
int l,r,s;
bool operator < (const node &h)const
{
if(l!=h.l)return l>h.l;
return r>h.r;
}
}tmp;
priority_queue<node>q;
int main()
{
int t;
read(t);
while(t--)
{
while (!q.empty()) q.pop();
read(n);read(m);
if(m==1){printf("true\n");continue;}
for(int i=1;i<=m;i++)
{
int l,r,s;
read(tmp.l);read(tmp.r);read(tmp.s);
q.push(tmp);
}
tmp=q.top();
q.pop();
while(!q.empty())
{
node tmp1;
tmp1=q.top();
q.pop();
if(tmp.l==tmp1.l)
{
if(tmp.r==tmp1.r)
{
if(tmp.s!=tmp1.s)
{printf("false\n");goto end;}
}
else
if(tmp.r<tmp1.r)
q.push((node) {tmp.r+1, tmp1.r, tmp1.s - tmp.s});
}
tmp = tmp1;
}
printf("true\n");
end:;
}
return 0;
}
带权并查集;
应该不是很好理解(反正我没懂)
给出[l,r]的区间和,相当于s[r]-s[l]
一旦已经知道了 s[a]-s[b],s[b]-s[c],显然再给出一条[a,c]就可以判断"账本的真假"了
将每条这样的信息(l,r,w),l,r放入一个集合中,
用并查集来维护,并维护cha[l]=s[root]-s[l],cha[r]=s[root]-s[r]
若 l,r已经在同一个集合中,就直接查询cha[l]-cha[r],判读与w是否相等
code:
#include<bits/stdc++.h>
int fa[105],cha[105];
int find(int x)
{
if(x!=fa[x])
{
int t=find(fa[x]);
cha[x]+=cha[fa[x]];
fa[x]=t;
}
return fa[x];
}
int main()
{
int T,n,m,i,x,y,z,flag;
scanf("%d",&T);
while (T--)
{
flag=0;
scanf("%d%d",&n,&m);
for(i=0;i<=n;i++)
{
fa[i]=i;
cha[i]=0;
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
x--;
if(find(x)!=find(y))
{
cha[fa[y]]=cha[x]-cha[y]-z;
fa[fa[y]]=fa[x];
}
else
if(cha[x]-cha[y]!=z) flag=1;
}
if(flag==0) printf("true\n"); else printf("false\n");
}
return 0;
}
区间DP
code(应该很好理解,就是时间有点傻逼O(n三方)):
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int f[101][101];
int main()
{
int w,n,m,p,s,t,v;
cin>>w;
for(int i=1;i<=w;i++)
{
memset(f,0,sizeof(f));
scanf("%d%d",&n,&m);
for(int j=1;j<=m;j++)
{
scanf("%d%d%d",&s,&t,&v);
f[s][t]=v;
}
p=1;
for(int j=2;j<=n;j++)
if(p)
for(int k=j-1;k>=1;k--)
if(p)
for(int l=k;l<j;l++)
if(f[k][l]&&f[l+1][j])
{
if(f[k][j])
{
if(f[k][j]!=f[k][l]+f[l+1][j])
{
p=0;
break;
}
}
else
f[k][j]=f[k][l]+f[l+1][j];
}
if(p==0)
cout<<"false"<<endl;
if(p==1)
cout<<"true"<<endl;
}
return 0;
}
差分约束
code(应该算套路吧):
最好写dfs版的spfa
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 500
#define MAXM 4000
#define MEMINF 0x3f
struct Edge
{
int to,nex,w;
Edge(){}
Edge(int _to, int _nex, int _w):to(_to),nex(_nex),w(_w){}
};
Edge e[MAXM+5];
int first[MAXN+5], dis[MAXN+5], tot, n, m, t, S;
bool book[MAXN+5];
inline void Add(int a, int b, int c)
{
e[tot] = Edge(b,first[a],c);
first[a] = tot++;
return;
}
bool SPFA(int p)
{
book[p] = true;
for(int u = first[p]; u+1; u = e[u].nex)
if(dis[e[u].to] > dis[p] + e[u].w)
{
dis[e[u].to] = dis[p] + e[u].w;
if(book[e[u].to] || !SPFA(e[u].to))
return false;
}
book[p] = false;
return true;
}
inline void Init()
{
scanf("%d%d",&n,&m);
memset(first,-1,sizeof(first));
memset(dis,MEMINF,sizeof(dis));
memset(book,false,sizeof(book));
S = 0, dis[S] = 0, tot = 0;
for(int s, t, v; m--; )
scanf("%d%d%d",&s,&t,&v), Add(t,s-1,-v), Add(s-1,t,v);
return;
}
int main()
{
for(scanf("%d",&t); t--; puts(SPFA(S) ? "true" : "false"))
Init();
return 0;
}