有2家公司,相同公司互相传送花费为0,不同公司花费为坐标之差的绝对值。给出公司分布,问从a到b的花费最少是多少。
如果ab公司相同,那么费用为0
否则的话费用为1
正确性显然
起始数字为1,然后经过如下变换:
1
121
1213121
121312141213121
…
问第n-1次变换后第k个位置的数是多少
可以发现这个数列满足二分的性质,那么从直接从n开始二分并且每一次都-1就可以了。
这道题的规律非常多啊。。。
注意:爆int
给出n,求使 2n=1x+1y+1z,x≠y y≠z x≠z 的x,y,z.
2n=1n+1n+1+1n(n+1).
“裂(三声)项相消法”。。
一棵树,每个点有一个权,从中选两个互不包含的子树使子树的和最大。
树形dp啊显然。。。
求出来子树的和了之后维护一个最大值和次大值就可以了。
坑点:刚开始的时候Impossible判错了,应该是-inf的问题。这种的点一定要谨慎。
#include
#include
#include
using namespace std;
#define LL long long
#define N 200005
const LL inf=1e18;
int n,x,y;
LL st,val[N],size[N],f[N][3];
int tot,point[N],nxt[N*2],v[N*2];
void add(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void dfs(int x,int fa)
{
size[x]=val[x];
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
dfs(v[i],x);
size[x]+=size[v[i]];
}
}
void treedp(int x,int fa)
{
f[x][1]=size[x];
LL Max=-inf,_Max=-inf;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
treedp(v[i],x);
f[x][1]=max(f[x][1],f[v[i]][1]);
f[x][2]=max(f[x][2],f[v[i]][2]);
if (f[v[i]][1]>=Max) _Max=Max,Max=f[v[i]][1];
else _Max=max(_Max,f[v[i]][1]);
}
if (Max!=-inf&&_Max!=-inf) f[x][2]=max(f[x][2],Max+_Max);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%I64d",&val[i]);
for (int i=1;iscanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(1,0);
memset(f,128,sizeof(f));
treedp(1,0);
if (f[1][2]<=-inf) puts("Impossible");
else printf("%I64d\n",f[1][2]);
}
给出一个只有1-8的数列,求一个最长的子序列,满足这个子序列里1-8这8个数任意两数的出现次数之差的绝对值都 ≤1 ,并且出现的相同的数字必须连续。
二分+dp。
可以发现如果1-8没有在数列里全部出现的话,那么出现的数只可以每一种选一个。这一条特判。
如果都出现过的话,每一个数的出现次数只可能有2种,并且这两个次数相差1。那么我们可以二分每一个数的出现次数中较小的那个数,然后在判断是否可行。
g(i,j,0/1) 表示从i开始选了mid/mid+1个j到达的位置。 f(i,j) 第一维状压表示8个数选的状态为i,选到了第j个位置(下一次从这个位置开始选)的子序列最长长度。这样的话就可以转移了。
需要用-1和-inf来判断各种状态是否合法。
刚开始犯了一个错误,就是判断是否合法的时候。界限应该宽松一些,否则轻微的调整都有可能出错。
#include
#include
#include
#include
using namespace std;
#define N 1005
int n,tot,ans,Max,inf;
int a[N],g[N][N][2],f[1<<9][N];
bool flag[10];
bool check(int mid)
{
memset(g,-1,sizeof(g));memset(f,128,sizeof(f));inf=f[0][0];
for (int i=1;i<=8;++i)
{
for (int j=1,cnt=0;j<=n;++j,cnt=0)
for (int k=j;k<=n;++k)
{
if (a[k]!=i) continue;
cnt++;
if (cnt==mid) g[j][i][0]=k;
if (cnt==mid+1) {g[j][i][1]=k;break;}
}
}
f[0][1]=0;
for (int i=0;i<1<<8;++i)
for (int j=1;j<=n;++j)
{
for (int k=1;k<=8;++k)
{
if ((i>>(k-1))&1) continue;
if (g[j][k][0]!=-1) f[i|(1<<(k-1))][g[j][k][0]+1]=max(f[i|(1<<(k-1))][g[j][k][0]+1],f[i][j]+mid);
if (g[j][k][1]!=-1) f[i|(1<<(k-1))][g[j][k][1]+1]=max(f[i|(1<<(k-1))][g[j][k][1]+1],f[i][j]+mid+1);
}
}
ans=inf;
for (int i=1;i<=n+1;++i)
ans=max(ans,f[(1<<8)-1][i]);
if (ans<=0) return false;
else return true;
}
int find()
{
int l=0,r=n/8+1,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid))
{
Max=max(Max,ans);
l=mid+1;
}
else r=mid-1;
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),flag[a[i]]=true;
for (int i=1;i<=8;++i) tot+=flag[i];
if (tot<8) {printf("%d\n",tot);return 0;}
find();
printf("%d\n",Max);
return 0;
}
遇到奇怪的问题不要着急,特判要想清楚,尤其是-inf之类的东西。
数学题不要慌,好好搞一搞。